01.3 平面與空間曲率計算#
本節目標#
- 從定義出發推導平面曲線的經典曲率公式 \(\kappa = \dfrac{|y''|}{(1+y'^2)^{3/2}}\) 。
- 用 SymPy 實作此公式(
diff、norm/sqrt、simplify),並處理化簡技巧。 - 掌握空間曲線曲率的一般公式 \(\kappa = \|\gamma'\times\gamma''\|/\|\gamma'\|^3\) 與 SymPy 的
cross實作。 - 實驗:拋物線、圓、螺旋線的曲率數值觀察,建立對曲率的「手感」。
幾何直覺:曲率到底在量什麼#
站在一條路上開車。方向盤轉得越兇、車速越快,你感受到的離心力越大。「曲率」就是把「方向盤轉得多兇」獨立出來的量——它不依賴車速,只依賴路本身。
用密切圓的語言:在曲線一點 \(P\) ,過 \(P\) 及鄰近兩點作一個圓,當鄰點趨近 \(P\) 時圓有極限位置(密切圓)。圓越小,曲線在 \(P\) 附近彎得越急。曲率 = 密切圓半徑的倒數。直線的密切圓半徑無窮大,曲率為 \(0\) 。
平面曲線曲率公式的推導#
設平面曲線寫成函數圖像 \(y = f(x)\) ,即參數化 \(\gamma(x) = (x, f(x))\) 。弧長參數下曲率是 \(\kappa = \|dT/ds\|\) ,我們把這個定義改寫到一般參數上。
步驟一:速度與速率
步驟二:加速度
步驟三:代入一般公式(下一小節推導) \(\kappa = \|\gamma'\times\gamma''\| / \|\gamma'\|^3\) 。平面外積的 \(z\) 分量就是 \(1\cdot f'' - f'\cdot 0 = f''\) ,因此
兩個立即的檢查:
- 若 \(f\) 是線性函數, \(f''=0\) ,曲率為 0。直線果然不彎。
- \(y'^2\) 項的存在表示:同一形狀「斜著放」時 \(y'\) 變大、分母變大,但分子也會變——最後幾何不變量扭回同樣的值(讀者可在實驗驗證)。
空間曲線的一般曲率公式#
回顧 01.2 節的結論:弧長參數下 \(\kappa = \|\gamma''(s)\|\) 。設 \(\gamma = \gamma(t)\) 是一般參數, \(s(t)\) 為弧長函數。由連鎖律:
其中 \(v=\gamma'\) 、 \(a=\gamma''\) 。取範數,利用 \(\|v\|^2\|a\|^2 - (v\cdot a)^2 = \|v\times a\|^2\) (Lagrange 恆等式),得:
這個形式的妙處: 1. 只需要一階與二階導數。 2. 完全不涉及弧長的反解。 3. 分母 \(\|\gamma'\|^3\) 自動補償了「走太快」造成的砍彎假象。
SymPy 實作:平面曲線#
圖像式曲線 \(y=f(x)\)#
import sympy as sp
x = sp.symbols('x', real=True)
def curvature_graph(f, x):
# 以 t=x 參數化:γ(t) = (t, f(t))
d1 = sp.diff(f, x)
d2 = sp.diff(f, x, 2)
kappa = sp.Abs(d2) / (1 + d1**2)**sp.Rational(3, 2)
return sp.simplify(kappa)
# 拋物線 y = x^2
f = x**2
print(curvature_graph(f, x)) # 2/(4*x**2 + 1)**(3/2)
注意在 \(x=0\) (頂點)處 \(\kappa = 2\) ,恰好等於「以曲率半徑 \(1/2\) 的圓貼合頂點」。
參數式平面曲線#
def curvature_parametric(gx, gy, t):
d1 = sp.Matrix([sp.diff(gx, t), sp.diff(gy, t)])
d2 = sp.Matrix([sp.diff(gx, t, 2), sp.diff(gy, t, 2)])
cross_z = sp.diff(gx, t)*sp.diff(gy, t, 2) - sp.diff(gy, t)*sp.diff(gx, t, 2)
speed = sp.sqrt(d1.dot(d1))
return sp.simplify(sp.Abs(cross_z) / speed**3)
t = sp.symbols('t', real=True)
# 圓 r = (cos t, sin t)
print(curvature_parametric(sp.cos(t), sp.sin(t), t)) # 應為 1(單位圓)
\|norm\| 的替代寫法#
SymPy 的 sp.norm(M) 等價於 sp.sqrt(M.dot(M)),對 2D/3D 向量都適用。
M = sp.Matrix([sp.cos(t), sp.sin(t), t])
print(sp.norm(M.diff(t))) # sqrt(2)
SymPy 實作:空間曲線與 cross product#
import sympy as sp
def curvature_3d(g, t):
v = g.diff(t) # γ'
a = v.diff(t) # γ''
cross = v.cross(a) # γ' × γ''
return sp.simplify(sp.sqrt(cross.dot(cross)) / sp.norm(v)**3)
# 3D 位置向量記號
X, Y, Z = sp.symbols('X Y Z', real=True)
helix = sp.Matrix([sp.cos(t), sp.sin(t), t])
print(curvature_3d(helix, t)) # 1/2
Matrix 的 .cross 方法完整支援三維外積。對 2D 曲線用 .cross 前先把向量「升維」到 3D(補 \(z=0\) ),或直接用上面的行列式寫法。
進一步:simplify 的化簡力#
有時 curvature_3d 直接輸出帶一堆根號與平方項的龐大式子。常用策略:
def curvature_3d_pretty(g, t):
v = g.diff(t); a = v.diff(t)
c = v.cross(a)
expr = sp.sqrt(sp.simplify(c.dot(c))) / sp.sqrt(sp.simplify(v.dot(v)))**3
return sp.simplify(sp.radsimp(expr))
或者先 sp.trigsimp 消去 \(\sin^2+\cos^2\) ,這對圓類與螺旋類曲線特別有效。
動手實驗#
實驗 1:三條曲線的曲率速查表
import sympy as sp
t = sp.symbols('t', real=True)
def kappa(g):
v = g.diff(t); a = v.diff(t)
c = v.cross(a)
return sp.simplify(sp.sqrt(sp.trigsimp(c.dot(c))) / sp.sqrt(sp.trigsimp(v.dot(v)))**3)
parabola_sp = sp.Matrix([t, t**2, 0]) # 空間參數化的拋物線
circle_3d = sp.Matrix([2*sp.cos(t), 2*sp.sin(t), 0]) # 半徑 2 的圓
helix = sp.Matrix([sp.cos(t), sp.sin(t), t]) # 單位螺旋線
for name, g in [("拋物線", parabola_sp), ("圓", circle_3d), ("螺旋線", helix)]:
print(name, ":", kappa(g))
觀察輸出:拋物線 \(\kappa(t)\) 隨 \(|t|\) 衰減(兩翼攤平);半徑 2 的圓 \(\kappa = 1/2\) (半徑的倒數);螺旋線 \(\kappa = 1/2\) (呼應公式 \(\kappa = a/(a^2+b^2)\) ,此處 \((a,b)=(1,1)\) )。
實驗 2:拋物線頂點曲率的極限檢查
x = sp.symbols('x', real=True)
f = x**2
k = sp.Abs(sp.diff(f, x, 2)) / (1 + sp.diff(f, x)**2)**sp.Rational(3, 2)
print("頂點 κ =", k.subs(x, 0)) # 2
print("頂點密切圓半徑 =", 1/k.subs(x, 0)) # 1/2
再比較:在頂點畫半徑 \(1/2\) 的圓(圓心 \((0, 1/2)\) ),與拋物線 \(\gamma(x)=(x, x^2)\) 用 matplotlib 疊圖,目視貼合程度。
實驗 3:參數化不變性的驗證
t2 = sp.symbols('t2', real=True)
# 同一條拋物線,兩種走法
g_slow = sp.Matrix([t, t**2, 0])
g_fast = sp.Matrix([2*t2, 4*t2**2, 0])
print(kappa(g_slow)) # 2/(4*t**2+1)**(3/2)
print(sp.simplify(kappa(g_fast).subs(t2, t/2))) # 化成 t 的式子後結果相同
實驗 4:(挑戰)用曲率公式反推一個生物問題
河口河道/河流的曲率大概滿足「波形 \(\kappa(s)\propto\sin\) 」的近似。給 \(\kappa(s)=\tfrac12\sin s\) 與 \(\tau=0\) ,使用 Frenet–Serret ODE(見 01.2 節)以 sp.dsolve 或數值求解重建曲線,畫出來看看像不像河道蜿蜒。
小結與練習#
本節重點
- 平面圖像曲線: \(\kappa = |y''| / (1+y'^2)^{3/2}\) ,分子量「二階彎」,分母補償「斜率放大」。
- 一般參數空間曲線: \(\kappa = \|\gamma'\times\gamma''\| / \|\gamma'\|^3\) ,由連鎖律與 Lagrange 恆等式導出。
- SymPy 實作三關鍵:
diff、.cross、simplify/trigsimp。 - 曲率是參數化不變量:換走法不變(SymPy 可輕鬆驗證)。
練習
- 推導:對極坐標曲線 \(r(\theta)\) ,曲率公式為 \(\kappa = \dfrac{|r^2 + 2r'^2 - r\,r''|}{(r^2+r'^2)^{3/2}}\) ,並用 SymPy 以對數螺線 \(r=e^\theta\) 驗證。
- 計算三次曲線 \(y = x^3\) 在原點的曲率,並用 01.2 節的想法解釋為什麼「曲率為零卻不平」(這是拐點)。
- 對曲線 \(\gamma(t) = (t, t^2, t^3)\) 求 \(\kappa(t)\) 與 \(B'\) 的範數(即 \(|\tau|\) ),繪 \(\kappa, \tau\) 隨 \(t\) 的關係。
- 證明: \(\kappa\) 在參數變換 \(t \mapsto \varphi(t)\) ( \(\varphi'>0\) )下不變。用 SymPy 對 \(\gamma(t)=(t, t^2, t^3)\) 與 \(\varphi(t)=t^3+t\) 驗證。
- 實驗:畫出 \(f(x) = \sin(x)\) 一個週期的曲率 \(\kappa(x)\) 曲線,找出曲率最大的位置(提示:
sp.solve(sp.diff(k, x), x)加數值驗證),與直覺對照。