01.2 Frenet 標架#
本節目標#
- 定義曲線的活動標架:切向量 \(T\) 、法向量 \(N\) 、副法向量 \(B\) ,並理解其幾何意義。
- 用「軌道火車」的比喻掌握/Frenet 標架沿曲線移動時的滾轉、俯仰行為。
- 定義曲率 \(\kappa\) 與撓率 \(\tau\) ,理解它們分別量化「轉彎」與「離開平面」。
- 整理 Frenet–Serret 公式,作為整本書的基本引擎。
- 學會非弧長參數下的曲率公式 \(\kappa = \|\gamma'\times\gamma''\|/\|\gamma'\|^3\) ,體會它的實用價值。
幾何直覺:一輛沿曲線行駛的火車#
想像一輛侏儸紀公園式的單軌小火車沿著空間曲線行駛。車廂上有一組固定在三個軸上的指針:
- **切向量 \(T\) **:指向車頭前進方向。
- **法向量 \(N\) **:指向「轉彎的那一側」——火車向哪邊拐彎, \(N\) 就指向哪邊。
- **副法向量 \(B = T \times N\) **:一根從車頂豎起的旗桿,垂直於行駛平面。
當火車進入彎道, \(T\) 變方向(=轉彎),變化的方向就是 \(N\) (=俯仰指向的側邊)。若曲線不在一個平面內(例如螺旋線),旗桿 \(B\) 還會緩緩傾斜、像飛機做一些方向舵動作一樣——這種「歪頭」就是撓率。飛機的比喻更貼切:曲率是「拉桿轉彎的急程度」,撓率是「滾轉離開水平面的速率」。
定義:切向量、法向量、副法向量#
假設 \(\gamma(s)\) 以弧長參數運行( \(\|\gamma'(s)\|=1\) )。定義:
說明:
- 因 \(\|T\|\equiv 1\) ,微分 \(\|T\|^2 = T\cdot T \equiv 1\) 得 \(T\cdot T' = 0\) ,故 \(T'\) 恆垂直 \(T\) , \(N\) 有定義(要求 \(T'\neq 0\) ,即曲率不為零的點)。
- \(N\) 是單位向量,且 \(N\cdot B = 0\) 、 \(T\cdot B = 0\) ,因此 \((T, N, B)\) 構成一組右手正交單位標架——像一組會沿曲線搬移並旋轉的座標軸。
曲率:轉彎的急程度#
定義(弧長參數下):
由 \(T' = \|T'\| N\) 立即得到核心關係:
直覺: \(T'\) 度量了前進方向改變得多快。直線的 \(T\) 是常數, \(\kappa=0\) ;半徑 \(r\) 的圓 \(\kappa = 1/r\) ——半徑越小彎越急。曲率單位是 \(1/\text{長度}\) ,其倒數 \(R = 1/\kappa\) 稱為曲率半徑。
撓率:離開平面的傾向#
對 \(B\) 微分(可證 \(B' \perp T\) 與 \(B' \perp B\) ,故 \(B'\) 平行 \(N\) ),定義撓率 \(\tau\) 使
負號是約定:當右手標架沿右旋螺旋線前進時, \(\tau > 0\) 。
幾何意義: \(\tau\) 度量 \(B\) (即「車頂旗桿」)傾斜的快慢,也就是曲線偏離自身密切平面的程度。
- 平面曲線: \(B\) 是常向量(整條曲線共享同一個平面法向), \(\tau = 0\) 。
- 螺旋線: \(\tau \neq 0\) ,且手性(左旋/右旋)決定 \(\tau\) 的正負。
Frenet–Serret 公式:一本移動座標的使用手冊#
把三條微分關係整理在一起:
第二式怎麼來?對 \(N = B\times T\) 微分(或直接從 \(N\cdot T=0\) , \(N\cdot B=0\) 與單位長度性質推出)可得 \(N' \perp N\) ,因此 \(N'\) 能用 \(T, B\) 展開: \(N' = c_1 T + c_2 B\) 。對 \(N'\cdot T\) 用 \(N\cdot T = 0\) 微分得 \(-N\cdot T' = -\kappa\) ,故 \(c_1 = -\kappa\) ;對 \(N'\cdot B\) 用 \(N\cdot B=0\) 微分得 \(-N\cdot B' = \tau\) ,故 \(c_2 = \tau\) 。
三個重要解讀:
- 標架的運動完全由 \(\kappa, \tau\) 兩個函數決定。這組 ODE 是「微分幾何的基本定理」的起點:給定 \(\kappa(s)>0\) 與 \(\tau(s)\) ,理論上可重建整條曲線(至剛體運動差別)。
- \(\kappa\) 與 \(\tau\) 都是參數化不變量(在保向的弧長參數變換下不變),是曲線真正的幾何性質。
- 它是線性 ODE——SymPy 的
dsolve或數值求解都可以玩。
非弧長參數的曲率公式:實務上的救生圈#
弧長參數化常求不出(如 01.1 節所見),所以我們需要一般參數 \(t\) 下的公式。利用連鎖律可以證明:
以及撓率:
便利之處:只需微分到二階(曲率)/三階(撓率),加上一個外積與範數,完全不必做「反解 \(s(t)\) 」這件通常不可能的事。下一節將用 SymPy 實作這些公式。
SymPy 實作#
定義工具函數#
import sympy as sp
def speed(g, t):
return sp.sqrt(g.diff(t).dot(g.diff(t)))
def curvature(g, t):
v = g.diff(t)
a = v.diff(t)
return sp.simplify(sp.sqrt(v.cross(a).dot(v.cross(a))) / speed(g, t)**3)
def torsion(g, t):
v = g.diff(t); a = v.diff(t); j = a.diff(t)
det = v.dot(a.cross(j))
return sp.simplify(det / sp.simplify(v.cross(a).dot(v.cross(a))))
以圓為例驗證 \(\kappa = 1/r\) , \(\tau = 0\)#
t = sp.symbols('t', real=True)
r = sp.symbols('r', positive=True)
circle = sp.Matrix([r*sp.cos(t), r*sp.sin(t), 0])
print("κ =", curvature(circle, t)) # 1/r
print("τ =", torsion(circle, t)) # 0 —— 平面曲線
Frenet 標架本身#
def frenet_frame(g, t):
v = g.diff(t)
T = sp.simplify(v / speed(g, t))
Tp = sp.simplify(T.diff(t))
N = sp.simplify(Tp / sp.sqrt(Tp.dot(Tp)))
B = sp.simplify(v.cross(g.diff(t, 2)) / sp.sqrt(v.cross(g.diff(t, 2)).dot(v.cross(g.diff(t, 2)))))
return T, N, B
helix = sp.Matrix([sp.cos(t), sp.sin(t), t]) # a=b=1 的螺旋線
T, N, B = frenet_frame(helix, t)
print("T =", T.T)
print("N =", N.T)
print("B =", B.T)
# 驗證正交單位性
print(sp.simplify(T.dot(N)), sp.simplify(T.dot(B)), sp.simplify(N.dot(B)))
print(sp.simplify(T.dot(T)), sp.simplify(N.dot(N)), sp.simplify(B.dot(B)))
輸出顯示所有內積為 0 與 1——(T, N, B) 是漂亮的正交標架。
用 Frenet 公式直接驗證 \(T'=\kappa N\) (一般參數版的連鎖律)#
k = curvature(helix, t)
dsdt = speed(helix, t) # d s / d t
# T 對 s 的導數 = (dT/dt) / (ds/dt)
T_prime_s = sp.simplify(T.diff(t) / dsdt)
kN = sp.simplify(k * N)
print(sp.simplify(T_prime_s - kN)) # 0 → T' = κN 成立
這一行程式碼驗證了:即使我們在一般參數 \(t\) 下工作, \(T'=\kappa N\) 在換算到弧長導數後依然精確成立。
動手實驗#
實驗 1:火車的方向盤——圓的 Frenet 標架
t, r = sp.symbols('t r', real=True, positive=True)
circle = sp.Matrix([r*sp.cos(t), r*sp.sin(t), 0])
T, N, B = frenet_frame(circle, t)
print("N =", N.T) # 應指向圓心 (-cos t, -sin t, 0)
print("B =", B.T) # 應為常向量 (0,0,1)
觀察: \(N\) 永遠指向圓心(火車轉彎的內側), \(B\) 是常向量(火車永遠保持水平,沒有滾轉)——這就是 \(\tau=0\) 的視覺意義。
實驗 2:螺旋線的 B 會傾斜
a, b = sp.symbols('a b', positive=True)
helix = sp.Matrix([a*sp.cos(t), a*sp.sin(t), b*t])
T, N, B = frenet_frame(helix, t)
print("B =", sp.simplify(B.T))
你會看到 \(B \approx (-b\sin t, b\cos t, a)\) 乘上常數——旗桿不再豎直,而是朝着運動方向傾斜固定角度。 \(b\) 越大(螺距越大)傾斜越厲害。
實驗 3:曲率半徑與「急彎」
對曲線 \(\gamma(t) = (t, t^2, 0)\) (拋物線)計算 \(\kappa(t)\) ,用 sp.plot 畫出 \(\kappa\) 隨 \(t\) 的圖形。確認在頂點 \(t=0\) 處曲率最大,且兩側衰減。物理對應:過山車在谷底承受最大的「G 力」。
實驗 4: \(\tau\) 的符號與手性
right = sp.Matrix([sp.cos(t), sp.sin(t), t]) # 右旋
left = sp.Matrix([sp.cos(t), sp.sin(t), -t]) # 左旋
print("右旋 τ =", torsion(right, t)) # 1/2
print("左旋 τ =", torsion(left, t)) # -1/2
對照觀察:鏡像(把 \(z\) 反號)會翻轉撓率的正負。你能否用「右手 vs. 左手螺旋」解釋這件事?
小結與練習#
本節重點
- \(T, N, B\) 是沿曲線搬移的右手正交單位標架(弧長參數下定義最乾淨)。
- 曲率 \(\kappa = \|T'\| = \|\gamma''\|\) 量化轉彎;撓率 \(\tau\) 量化離開平面的傾向。
- Frenet–Serret 公式 \(T'=\kappa N,\ N'=-\kappa T+\tau B,\ B'=-\tau N\) 說明:標架的運動完全由 \(\kappa,\tau\) 決定。
- 非弧長參數的公式 \(\kappa=\|\gamma'\times\gamma''\|/\|\gamma'\|^3\) 讓實務計算不必反解 \(s(t)\) 。
練習
- 用 SymPy 驗證:對任何弧長參數曲線, \(T\cdot N' = -\kappa\) (提示:從 \(N\cdot T=0\) 微分出發)。
- 證明 mixing 關係 \(\kappa\tau = -\det(T', N', B')/(\|T\|\cdots)\) 的平面版本:平面曲線必有 \(\tau=0\) 。用 SymPy 對 \(\gamma(t)=(\cos t,\sin t,f(t))\) ( \(f\) 為任意函數)檢查 \(B\) 與 \(z\) 軸的關係。
- 對 Nephroid 或 Cardioid 之類的平面曲線,用本節
curvature函數找出最大曲率的位置。 - 用
sp.dsolve(或手算)求解 \(\kappa\) 為常數 \(c\) 、 \(\tau=0\) 的 Frenet ODE,證明解是半徑 \(1/c\) 的圓。 - 實驗:寫一個函數
curvature_radius(g, t)回傳 \(1/\kappa\) ,對拋物線畫 \(R(t)\) 圖,並在頂點附近按比例縮小,觀察它逼近「密切圓」的程度。