模型預測控制入門:從有限時域最佳化到即時求解
- mpc
- optimal-control
- quadratic-programming
- state-space
- receding-horizon
問題定義
PID 控制器(見PID 控制器完整拆解)只根據「現在」的誤差反應,不知道系統未來會怎麼演變,也沒有機制原生處理致動器極限或狀態限制。模型預測控制(Model Predictive Control, MPC)從根本上換了一個思路:在每個控制週期,用一個系統動態模型,預測接下來一段有限時域內、在不同控制輸入序列下系統會怎麼演變,然後解一個最佳化問題,找出讓這段時域內某個成本函數(通常同時衡量「跟目標的偏差」與「控制輸出的大小」)最小的控制序列——但只執行這個序列的第一步,下一個控制週期用新的量測值重新預測、重新求解。這個「只執行第一步、每步都重新求解」的策略稱為遞推時域(Receding Horizon),是 MPC 名字裡「Predictive」真正的實作方式,也是它跟開迴路軌跡最佳化最關鍵的差異——每一步都用最新的量測值修正模型誤差與外部擾動,本質上仍然是一種反饋控制。
狀態空間模型
線性 MPC 建立在離散時間線性狀態空間模型上:
是系統狀態(例如移動機器人的位置、速度;機械手臂的關節角度、角速度), 是控制輸入,、 是描述系統動態的矩陣。這個模型可能是系統真實動態的線性近似(例如在某個工作點附近對非線性動態做泰勒展開取一階項),也可能是本身就接近線性的系統(例如倒單擺在接近直立點附近的動態)。
有限時域最佳化問題
在第 步,MPC 要解的問題是:給定目前狀態 ,找出未來 步的控制序列 ,使得以下二次型成本函數最小:
其中 。 是狀態誤差權重矩陣, 是控制輸出權重矩陣, 是時域終點的終端成本權重(通常設得比 大,鼓勵系統在時域結束時已經接近目標,這對確保遞推時域策略的穩定性有實質幫助)。
這個成本函數的設計直接體現了控制問題裡最基本的 trade-off: 相對 越大,控制器越積極地消除狀態誤差、但也會用更大的控制輸出(可能更耗能、動作更劇烈); 相對 越大,控制輸出越平滑保守、但跟蹤目標的速度變慢。這組權重的選擇是 MPC 實務調參的核心工作,概念上跟 PID 調整 、、 三個增益的角色類似,但因為是矩陣形式,還能對狀態向量裡不同分量(例如「位置誤差」跟「速度誤差」)給予不同的重視程度。
約束條件是 MPC 相對 PID 最核心的結構性優勢:致動器極限 、狀態限制 (例如關節角度不能超過機構極限、移動平台速度不能超過安全上限)直接寫進最佳化問題的約束條件,求解器保證輸出的控制序列滿足這些限制;PID 沒有這種機制,只能在輸出端事後做飽和限幅(見 PID 控制器完整拆解 中的抗飽和一節),這是被動補救,不是主動規劃。
凝聚形式與二次規劃求解
上述問題可以透過把未來狀態 全部用初始狀態 與控制序列表示(反覆代入狀態方程式),消去狀態變數,只留下控制序列 作為決策變數,這個過程稱為凝聚(condensing)。凝聚後,原本的問題化簡成標準二次規劃(Quadratic Programming, QP)形式:
、、、 都可以從 、、、、 與約束邊界事先推導出解析公式(矩陣運算,不含未知數的迭代求解),每個控制週期只需要代入最新的 重新算出 (、、 在線性時不變系統下通常整個控制過程都不變,可以離線算好),再呼叫標準 QP 求解器求解——這也是為什麼線性 MPC 即使要在每個控制週期重新求解一個最佳化問題,仍然能做到相對高頻率(幾十到幾百 Hz)的即時控制:QP 求解器對凝聚後的中等規模問題,求解時間通常在毫秒等級。
線性 MPC vs 非線性 MPC
線性 MPC(Linear MPC, LMPC) 假設系統動態可以用線性模型(或分段線性近似)描述,優點是能化簡成上述標準 QP 問題,QP 是凸最佳化問題,有成熟且高效的求解器(可以保證找到全域最佳解,求解時間也相對可預測),這是線性 MPC 能在中高頻率控制迴路裡即時運行的關鍵原因。缺點是對高度非線性的系統(大幅度姿態變化、接觸力學)誤差會顯著累積。
非線性 MPC(Nonlinear MPC, NMPC) 直接對非線性動態模型 求解,成本函數與約束也可能是非線性的,問題不再是凸的,沒有保證能找到全域最佳解,常見求解方法是序列凸規劃(Sequential Convex Programming, SCP,每一步在目前解附近線性化再解一個 QP,反覆迭代逼近非線性問題的解)、iLQR(iterative Linear Quadratic Regulator)或 DDP(Differential Dynamic Programming)。四足與雙足機器人的動態步態規劃是 NMPC 近幾年最活躍的應用場景之一——足式機器人的接觸力學(腳何時觸地、摩擦力錐約束)本質上是高度非線性甚至非光滑的,線性近似會產生顯著誤差,直到近幾年隨著嵌入式運算能力提升與更高效的 NMPC 求解器(例如 MIT Cheetah 系列與其後續工作採用的凸 MPC 簡化版本)出現,NMPC/簡化非線性 MPC 才逐漸能在四足機器人上做到全身即時控制。
程式碼範例
以下用一個離散雙積分器系統(狀態是位置與速度,控制輸入是加速度,是移動平台速度控制、或關節角速度控制的簡化模型)示範線性 MPC 的凝聚形式與遞推時域迴圈:
import numpy as np
from scipy.optimize import minimize
def build_prediction_matrices(A, B, N):
"""建立凝聚形式:x_traj = Sx @ x0 + Su @ u_seq"""
n, m = A.shape[0], B.shape[1]
Sx = np.zeros((n * N, n))
Su = np.zeros((n * N, m * N))
A_power = np.eye(n)
for i in range(N):
A_power = A_power @ A
Sx[i * n:(i + 1) * n, :] = A_power
for i in range(N):
for j in range(i + 1):
A_power_ij = np.linalg.matrix_power(A, i - j)
Su[i * n:(i + 1) * n, j * m:(j + 1) * m] = A_power_ij @ B
return Sx, Su
def mpc_step(x0, x_ref, A, B, Q, R, N, u_min, u_max):
"""解一步 MPC:回傳這一步該執行的控制輸入(時域內第一步)"""
n, m = A.shape[0], B.shape[1]
Sx, Su = build_prediction_matrices(A, B, N)
Q_bar = np.kron(np.eye(N), Q)
R_bar = np.kron(np.eye(N), R)
x_ref_bar = np.tile(x_ref, N)
def cost(u_flat):
x_traj = Sx @ x0 + Su @ u_flat
state_error = x_traj - x_ref_bar
return state_error @ Q_bar @ state_error + u_flat @ R_bar @ u_flat
bounds = [(u_min, u_max)] * (m * N)
u0_guess = np.zeros(m * N)
result = minimize(cost, u0_guess, bounds=bounds, method="SLSQP")
u_seq = result.x.reshape(N, m)
return u_seq[0] # 遞推時域:只執行第一步
def simulate_receding_horizon(A, B, Q, R, N, x0, x_ref, u_min, u_max, steps):
"""完整遞推時域模擬迴圈"""
x = x0.copy()
trajectory = [x.copy()]
for _ in range(steps):
u = mpc_step(x, x_ref, A, B, Q, R, N, u_min, u_max)
x = A @ x + B @ u
trajectory.append(x.copy())
return np.array(trajectory)
# 雙積分器範例:狀態 [位置, 速度],控制輸入 [加速度]
dt = 0.1
A = np.array([[1, dt], [0, 1]])
B = np.array([[0.5 * dt ** 2], [dt]])
Q = np.diag([10.0, 1.0])
R = np.array([[0.1]])
traj = simulate_receding_horizon(
A, B, Q, R, N=10,
x0=np.array([0.0, 0.0]),
x_ref=np.array([5.0, 0.0]),
u_min=-2.0, u_max=2.0,
steps=50,
)
這裡刻意用 scipy.optimize.minimize 而不是專門的 QP 求解器,是為了讓程式碼保持在標準函式庫範圍內、方便直接執行理解流程;正式的即時控制系統會改用專門的 QP 求解器(例如 OSQP、qpOASES),對凝聚後的標準 QP 形式,這些求解器的求解速度比通用非線性最佳化器快上一到兩個數量級,這個差異在需要達到百 Hz 等級控制頻率的應用裡是決定性的。
常見錯誤
- 時域長度沒有跟控制頻率一起考慮:時域拉長能提升前瞻性,但求解時間也隨之增加,在即時控制迴路裡沒有先確認求解時間在控制週期預算內,會直接導致控制迴路跟不上。
- 終端成本 設得跟 一樣、或乾脆省略:沒有適當放大的終端成本,遞推時域策略的穩定性保證會變弱,系統可能在時域邊界附近出現不必要的震盪傾向。
- 拿線性 MPC 直接套用在高度非線性的系統上、卻不做任何重新線性化:只在初始工作點線性化一次,系統狀態偏離工作點越遠,模型誤差累積越明顯,通常需要在每個控制週期用目前狀態重新線性化(形成一種簡化版的非線性 MPC),而不是全程套用同一組固定的 、。
- 把 、 當成獨立於系統物理單位的抽象數字亂調:狀態向量裡不同分量的物理單位差異很大(例如角度用弧度、角速度用弧度/秒), 矩陣對角線的相對大小需要考慮這些單位差異,否則某個分量的誤差在數值上天生就比另一個分量大,會不成比例地主導最佳化結果。
- 忽略 QP 求解器可能回傳不可行(infeasible)的情況:當約束條件互相矛盾(例如目前狀態已經逼近極限、時域內物理上不可能同時滿足所有狀態與控制限制)時,求解器可能無解,沒有處理這種邊界情況的容錯機制(例如放寬部分軟約束),控制迴路會直接卡死。
延伸閱讀
常見問題
- MPC 跟 PID 最本質的差異是什麼?不只是「比較複雜」而已嗎?
- 本質差異在於 PID 是純粹的反饋控制器——它只看「現在」的誤差(以及誤差的歷史累積與變化率),完全不知道系統未來會怎麼演變,也不知道致動器有沒有極限;MPC 則是在每一步都用系統模型「預測」未來一段時間(時域)的行為,然後解一個最佳化問題找出讓未來這段時間內成本最小的控制序列,只執行第一步就重新預測。這個差異帶來兩個 PID 結構上做不到的能力:一是原生處理多輸入多輸出之間的耦合(例如四軸飛行器四個馬達轉速互相耦合影響姿態與高度,PID 通常要拆成多個獨立迴圈硬湊,MPC 可以在同一個最佳化問題裡處理耦合關係),二是原生把致動器極限與狀態限制寫進最佳化問題的約束條件,而不是像 PID 那樣要另外加飽和限幅這種事後補救機制。
- 時域(horizon)越長,控制效果一定越好嗎?
- 不一定,而且有明顯的 trade-off。時域越長,控制器能「看得越遠」,理論上越接近全時域最佳解,穩定性保證通常也更好;但代價是每一步都要解一個規模隨時域長度增大的二次規劃問題,計算成本隨時域長度大致呈平方到立方成長(取決於是否用凝聚形式),在需要毫秒等級即時反應的控制迴路裡,時域拉太長可能導致單步求解時間超過控制週期,直接讓即時性破功。實務上時域長度是一個需要跟控制頻率一起權衡的超參數,太短會讓控制器變得短視(跟只看一步的貪婪法差異不大,可能在需要提前煞車的情境下反應不及),太長則犧牲即時性,通常會先抓一個大約覆蓋系統主要動態時間常數的時域長度,再依實際運算預算調整。
- 非線性 MPC(NMPC)什麼時候才是必要的,用線性 MPC 硬做行不行?
- 如果系統動態在你關心的工作範圍內非線性程度不高,或者可以在每個控制週期附近做線性化近似(例如足式機器人在小幅擾動附近的姿態控制),線性 MPC 配合適當頻率的重新線性化通常已經夠用,計算成本也遠低於真正的非線性 MPC。但像四足或雙足機器人的動態步態規劃、需要大幅度姿態變化的機動動作,系統動態(尤其是接觸力學與轉動慣性耦合)的非線性程度高到線性近似會產生顯著誤差,這種情況才真正需要非線性 MPC(通常用序列凸規劃 SCP、iLQR 或 DDP 這類方法求解),代價是計算成本高出一個數量級以上,這也是為什麼直到近幾年 GPU/嵌入式運算能力提升,NMPC 才逐漸在腿式機器人的即時控制迴路裡變得可行。