§2 CARIMA 与丢番图递推
GPC 基于 CARIMA(Controlled AutoRegressive Integrated Moving Average)模型(教材式5.1):
$$ A(q^{-1})\, y(t) = B(q^{-1})\, u(t-k) + \frac{C(q^{-1})}{\Delta}\, \xi(t) \tag{5.1}$$其中 $\Delta = 1 - q^{-1}$ 是差分算子,$C(q^{-1})=1$(简化)。两边乘 $\Delta$,得等价形式(教材式5.2):
$$ A(q^{-1})\,\Delta y(t) = B(q^{-1})\,\Delta u(t-k) + \xi(t) \tag{5.2}$$- $A(q^{-1}) = 1 + a_1 q^{-1} + \cdots + a_{n_a} q^{-n_a}$,阶次 $n_a$
- $B(q^{-1}) = b_0 + b_1 q^{-1} + \cdots + b_{n_b} q^{-n_b}$,阶次 $n_b$
- $k$:纯时延步数
水箱参数:$n_a=1,\ n_b=0,\ k=24$,$A=[1,\ a_1]$,$B=[b_0]$。
对每个预测步 $j = 1, 2, \ldots, p$,求多项式对 $(E_j,\, F_j)$ 满足(教材式5.3):
$$ 1 = E_j(q^{-1})\, A(q^{-1})\,\Delta + q^{-j}\, F_j(q^{-1}) \tag{5.3}$$各多项式的阶次(教材式5.4):
| 多项式 | 阶次 | 项数 |
|---|---|---|
| $E_j(q^{-1})$ | $j-1$ | $j$ 项 |
| $F_j(q^{-1})$ | $n_a$ | $n_a+1$ 项 |
由此定义(教材式5.5):
$$ G_j(q^{-1}) = E_j(q^{-1})\, B(q^{-1}) \tag{5.5}$$$G_j$ 的阶次 $= (j-1) + n_b = n_b + j - 1$,长度 $= n_b + j$($n_b=0$ 时长度 $= j$)。
起点 $j=1$(教材式5.9~5.11):
$$ E_1 = 1, \quad G_1 = B(q^{-1}) \tag{5.11}$$ $$ F_1(q^{-1}) = (1-a_1) + (a_1-a_2)q^{-1} + \cdots + (a_{n_a-1}-a_{n_a})q^{-(n_a-1)} + a_{n_a} q^{-n_a} \tag{5.10}$$即 $f_1^i = a_i - a_{i+1}$($i=0,\ldots,n_a-1$,$a_0=1$),$f_1^{n_a} = a_{n_a}$。
递推 $j-1 \to j$(令 $e_{j-1} = f_{j-1}^0$,教材式5.17~5.19):
$$ e_{j-1} = f_{j-1}^0 \tag{5.17}$$ $$ f_j^i = f_{j-1}^{i+1} - e_{j-1}(a_{i+1} - a_i), \quad i = 0, 1, \ldots, n_a-1 \quad (a_0 = 1) \tag{5.18a}$$ $$ f_j^{n_a} = e_{j-1} \cdot a_{n_a} \tag{5.18b}$$ $$ g_j^i = g_{j-1}^i, \quad 0 \leq i \leq j-2 \quad \text{(低段不变)} \tag{5.19a}$$ $$ g_j^{n_b+j-1} = e_{j-1} \cdot b_{n_b} \quad \text{(最高项,}n_b=0\text{ 时即 }e_{j-1} \cdot b_0\text{)} \tag{5.19c}$$
def diophantine(A, b0, p):
a = A # a[0]=1, a[1]=a_1
# j=1 初值(式5.10)
F[i] = a[i] - a[i+1] # i=0..na-1
F[na] = a[na]
# 递推(式5.18a, 5.18b)
F_new[i] = F_prev[i+1] - e*(a[i+1]-a[i])
F_new[na] = e * a[na]
# G_j(式5.19,nb=0)
G_new[:j-1] = G_prev # 低段继承
G_new[j-1] = e * b0 # 最高项
$A = [1,\ a_1] = [1,\ -0.9846]$,$b_0 = 0.024361$,$n_a=1,\ n_b=0$:
$j=1$ 初值(教材式5.10,$n_a=1$):
$$ F_1 = [(1-a_1),\ a_1] = [1.9846,\ -0.9846] \tag{2.1}$$ $$ G_1 = [b_0] = [0.024361] \tag{2.2}$$$j=2$($e_1 = f_1^0 = 1.9846$):
$$ f_2^0 = f_1^1 - e_1(a_1 - a_0) = -0.9846 - 1.9846 \times (-0.9846 - 1) = 2.9540 \tag{2.3}$$ $$ f_2^1 = e_1 \cdot a_1 = 1.9846 \times (-0.9846) = -1.9540 \tag{2.4}$$ $$ G_2 = [0.024361,\ e_1 \cdot b_0] = [0.024361,\ 0.048347] \tag{2.5}$$F_list[0] = [1.9846, -0.9846],G_list[1] = [0.024361, 0.048347],与手算一致。$G_j$ 的前 $j$ 项 = 系统(5.1)的阶跃响应系数(教材式5.16 的关键性质)。
$j$ 步最优预测(教材式5.8):
$$ \hat y_{t+j|t} = F_j(q^{-1})\, y(t) + G_j(q^{-1})\, \Delta u(t+j-k) \tag{5.8}$$将 $G_j \Delta u$ 分为已知(历史)和未知(未来)两部分,得预测分解(教材式5.21):
$$ \hat{\mathbf{Y}} = \mathbf{Y}^1 + \mathbf{G}\,\Delta\mathbf{U} \tag{5.21}$$自由响应 $\mathbf{Y}^1$(已知部分):
$$ Y^1_j = F_j(q^{-1})\, y(t) + \sum_{m: j-k-m<0} G_j[m]\, \Delta u(t+j-k-m) \tag{2.6}$$即 $Y^1_j = F_j \cdot y_t$($F_j$ 项)加上 $G_j$ 中对应历史 $\Delta u$ 的贡献。
$n_a=1$ 时 $F_j$ 有 2 项:$Y^1_j \supseteq f_j^0 y(t) + f_j^1 y(t-1)$。
$\mathbf{G}$ 矩阵(教材式5.22):$p \times p$ 下三角,$G[i,j] = g_{i-j}$($i \geq j$,否则0),$g_i = G_{i+1}[i]$。
def free_response(F_list, G_list, y_buf, du_buf, p, k):
for j in range(p):
# F_j * y_t(式5.18,F_j有2项)
fy = f_j^0*y(t) + f_j^1*y(t-1)
# G_j 中历史 Delta*u 的贡献
# lag = k + m - j,lag>0 时是历史值
gu = sum(G_j[m]*du_buf[-lag] for m if lag>0)
Y1[j] = fy + gu
错误现象:仿真中 $y$ 完全不跟踪 SP,控制量几乎不动。
根本原因:第一版代码把 $\tilde A = A\Delta$ 当成新的 $A$ 来递推,$F_j$ 有3项($n_a+2$),而教材式(5.4b) 明确 $F_j$ 阶次 $= n_a$,有 $n_a+1$ 项。
| 错误版本 | 正确版本(教材) | |
|---|---|---|
| 递推对象 | $\tilde A = A\Delta$(2阶) | 原始 $A$(1阶) |
| $F_j$ 项数 | $n_a + 2 = 3$ | $n_a + 1 = 2$(式5.4b) |
| $F_1$ 初值 | $[\tilde A_0-\tilde A_1,\ \tilde A_1-\tilde A_2,\ \tilde A_2]$ | $[(1-a_1),\ a_1]$(式5.10) |
# 正确:用原始 A 的系数递推,F_j 有 na+1=2 项
F[i] = a[i] - a[i+1] # i=0..na-1,a_0=1
F[na] = a[na] # 式5.10
错误现象:C++ 版 G2 从第 3 项开始与 Python 不一致,denom 差约 4 倍。
根本原因:Python 中 $G_j$ 长度为 $j$,最高项在索引 $j-1$;C++ 递推时多分配了一位,最高项写到了索引 $j$ 而不是 $j-1$,导致 $G\_\text{mat}[j][i]$ 的索引偏移了 1。
| $j=2$ 时 | Python $G_\text{list}[1]$ | C++ 错误版 |
|---|---|---|
| 最高项位置 | 索引 1($G[1]=0.04835$) | 索引 2(错位) |
// 正确:G_new 长度 j+1,最高项在索引 j(对应 Python G_j[j-1])
double G_new[P] = {0};
for (int kk = 0; kk < jj; kk++) G_new[kk] = G_cur[kk];
G_new[jj] = e * b0; // 最高项在 jj
// G_mat[j][i] = G_new[j-i]
G_mat[jj][ii] = (idx >= 0) ? G_new[idx] : 0.0;