1. 什么是卡尔曼滤波

最佳线性滤波理论起源于二十世纪40年代美国科学家Wiener和前苏联科学家KOnMoropOB等人的研究工作,后人统称为维纳滤波理论。60年代Kalman把状态空间模型引入滤波理论,并导出了一套递推估计算法,后人称之为卡尔曼滤波理论。卡尔曼滤波是以最小均方误差为估计的最佳准则,来寻求一套递推估计的算法,其基本思想是:采用信号与噪声的状态空间模型,利用前一时刻地估计值和现时刻的观测值来更新对状态变量的估计,求出现时刻的估计值。它适合于实时处理和计算机运算。

卡尔曼滤波的实质是由量测值重构系统的状态向量。它以“预测一实测一修正”的顺序递推,根据系统的量测值来消除随机干扰,再现系统的状态,或根据系统的量测值从被污染的系统中恢复系统的本来面目。

卡尔曼滤波的形式
1、模型要求
卡尔曼滤波要求模型已知。即模型的结构与参数已知,且随机向量的统计特征已知。
2、卡尔曼滤波分类
记r,的向量函数:

X ^ ( k / j ) = E [ X ( k ) / Y j ] \hat{X}(k/j)=E[X(k)/Y^j] X^(k/j)=E[X(k)/Yj]

为状态 X ( k ) X(k) X(k)的估计量,分三种情况:

  • 当k>j时,称为预测;
  • 当k=j时,称为滤波;
  • 当k<j时,称为平滑。

2. 预测步骤

卡尔曼滤波(Kalman Filtering)是一种用于估计系统状态的递归算法,它能够通过融合传感器测量数据和系统模型来提供最优的状态估计。该算法由数学家R.E.卡尔曼于1960年提出,广泛应用于控制系统、导航系统、机器人、信号处理等领域。

卡尔曼滤波的基本原理是通过两个步骤来更新状态估计:预测步骤和更新步骤。

  1. 预测步骤(Prediction Step):
    在预测步骤中,根据系统的动态模型,利用上一时刻的状态估计和系统的控制输入,预测当前时刻的状态。同时,通过对上一时刻的状态协方差矩阵进行预测,得到当前时刻的预测误差协方差矩阵。

  2. 更新步骤(Update Step):
    在更新步骤中,通过利用传感器测量数据来修正预测的状态估计。首先,通过观测模型将当前时刻的状态估计映射到传感器测量空间中,得到预测的测量值。然后,计算测量残差(预测测量值与实际测量值之间的差异),并利用残差来修正预测的状态估计和误差协方差矩阵。卡尔曼滤波器会根据测量残差的大小自适应地调整状态估计的权重,更加重视测量精度高的传感器数据。

卡尔曼滤波算法的核心是状态估计和协方差更新。状态估计包括状态向量和协方差矩阵,其中状态向量描述系统的状态,协方差矩阵描述状态估计的不确定性。通过不断迭代预测和更新步骤,卡尔曼滤波器能够提供系统状态的最优估计,并且具有递归性质,可以在实时应用中高效地进行状态估计。

卡尔曼滤波算法的优势在于它能够有效地处理线性系统和高斯噪声,并且在噪声较小、系统模型准确的情况下,能够提供最优的估计结果。然而,在非线性系统和非高斯噪声的情况下,卡尔曼滤波器的性能可能下降。为了解决这些问题,后续发展出了扩展卡

3. 卡尔曼滤波原理

卡尔曼滤波器的计算公式可以分为预测步骤和更新步骤,下面是详细的解释:

3.1. 预测步骤(Prediction Step):

预测步骤用于根据系统模型和上一时刻的状态估计来预测当前时刻的状态。

公式1:预测的状态向量:

x ^ t − = F t x ^ t − 1 + B t u t \hat{x}_{t}^{-} = F_{t}\hat{x}_{t-1} + B_{t}u_{t} x^t=Ftx^t1+Btut

其中,

  • x ^ t − \hat{x}_{t}^{-} x^t 是当前时刻的状态向量的预测估计值。
  • F t F_{t} Ft 是状态转移矩阵,描述系统状态从上一时刻到当前时刻的变化。
  • x ^ t − 1 \hat{x}_{t-1} x^t1 是上一时刻的状态向量的估计值。
  • B t B_{t} Bt 是输入控制矩阵,描述外部输入对系统状态的影响。
  • u t u_{t} ut 是当前时刻的外部输入。

公式2:预测的误差协方差矩阵:

P t − = F t P t − 1 F t T + Q t P_{t}^{-} = F_{t}P_{t-1}F_{t}^T + Q_{t} Pt=FtPt1FtT+Qt

其中,

  • P t P_{t} Pt 是当前时刻的状态估计误差协方差矩阵的预测值。
  • P t − 1 P_{t-1} Pt1 是上一时刻的状态估计误差协方差矩阵。
  • Q t Q_{t} Qt 是系统过程噪声的协方差矩阵,描述系统状态模型中的不确定性。

3.2. 更新步骤(Update Step):

更新步骤用于根据传感器测量数据来修正预测的状态估计。

公式3:卡尔曼增益:

K t = P t − H t T ( H t P t − H t T + R t ) − 1 K_{t} = P_{t}^{-}H_{t}^T(H_{t}P_{t}^{-}H_{t}^T + R_{t})^{-1} Kt=PtHtT(HtPtHtT+Rt)1

其中,

  • K t K_{t} Kt 是卡尔曼增益,用于权衡预测值和测量值对状态估计的修正。
  • R t R_{t} Rt 是测量噪声的协方差矩阵

公式4:更新的状态估计:
x ^ t = x ^ t − + K t ( z t − H t x ^ t − ) \hat{x}_{t} = \hat{x}_{t}^{-} + K_{t}(z_{t} - H_{t}\hat{x}_{t}^{-}) x^t=x^t+Kt(ztHtx^t)

其中,

  • x ^ t \hat{x}_{t} x^t 是当前时刻的状态向量的修正估计值。
  • z t z_{t} zt 是当前时刻的测量值。
  • H t H_{t} Ht 是观测模型矩阵,将状态向量映射到测量空间。

公式5:更新的误差协方差矩阵:
P t = ( I − K t H t ) P t − P_{t} = (I - K_{t}H_{t})P_{t}^{-} Pt=(IKtHt)Pt

其中,

  • P t P_{t} Pt 是当前时刻的状态估计误差协方差矩阵的修正值。
  • I I I 是单位矩阵。

以上就是卡尔曼滤波器的计算公式。通过不断迭代预测和更新步骤,可以得到最优的系统状态估计。值得注意的是,卡尔曼滤波器的性能依赖于系统模型的准确性和噪声统计特性的准确估计。在实际应用中,通常需要根据具体情况进行参数调整和噪声模型的估计。

4. 预测代码

4.1. 常用运动位置预测

案例:对均加速直线运动的位置进行降噪处理,加速度为1,初始时间为0,初始位置为0,观测时间为7秒,每0.1秒观测一次,设误差为白噪声 N ( 0 , 1 ) N(0,1) N(0,1)

公式1中,
x ^ t − 1 \hat{x}_{t-1} x^t1 是当前时刻的状态向量(描述位置和速度)的预测估计值:

x ^ t − 1 = [ d t − 1 v t − 1 ] \hat{x}_{t-1} = \begin{bmatrix} d_{t-1}\\ v_{t-1} \end{bmatrix} x^t1=[dt1vt1]

F t F_{t} Ft 是状态转移矩阵:

F t = [ 1 Δ t 0 1 ] F_t = \begin{bmatrix} 1 & \Delta t\\ 0 & 1 \end{bmatrix} Ft=[10Δt1]

B t B_{t} Bt 是输入控制矩阵(中学物理中速度、加速度计算公式):

B t = [ 1 2 Δ t 2 0 0 Δ t ] B_t = \begin{bmatrix} \frac{1}{2} \Delta t^2 & 0\\ 0 & \Delta t \end{bmatrix} Bt=[21Δt200Δt]

实现代码如下:

import numpy as np
import matplotlib.pyplot as plt
delta_t = 0.1                               # 每秒钟采一次样
end_t = 7                                   # 时间长度
time_t = end_t * 10                         # 采样次数
t = np.arange(0, end_t, delta_t)            # 设置时间数组
v_var = 1                                   # 测量噪声的方差
v_noise = np.round(np.random.normal(0, v_var, time_t), 2)# 定义测量噪声
a=1                                         #加速度
vn=np.add((1 / 2*a * t ** 2) , v_noise)     # 定义仪器测量的位置
v=a*t  #定义速度数组
 
x = np.mat([vn,v])                          # 定义状态矩阵
xc = np.shape(x)[1]                         #调用状态矩阵列数
u1=np.linspace(a,a,xc)
u = np.mat([u1,u1])                         # 定义外界对系统作用矩阵
F = np.mat([[1, delta_t], [0, 1]])          # 定义状态转移矩阵
B = np.mat([[1 / 2 * (delta_t ** 2),0],[0,delta_t]]) # 定义输入控制矩阵
P = np.mat([[1, 0], [0, 1]])                # 定义初始状态协方差矩阵
Q = np.mat([[0.001, 0], [0, 0.001]])        # 定义状态转移(预测噪声)协方差矩阵
H = np.mat([1, 0])                          # 定义观测矩阵
R = np.mat([1])                             # 定义观测噪声协方差矩阵

x_hat = x.T[0].T                            # 抽取预测优化值的初始状态值
X_mat = x.copy()                        # 初始化记录系统优化状态值的矩阵(浅拷贝)
Z = H * x

xr = np.shape(x)[0]                     # 调用状态矩阵行数
xc = np.shape(x)[1]                     # 调用状态矩阵列数

# 卡尔曼滤波
for i in range(1,xc):
    # 预测步骤
    x_hat_minus = F.dot(x_hat) + B.dot(u)  # 预测的状态估计
    P_minus = F.dot(P).dot(F.T) + Q  # 预测的误差协方差
    H_t = P_minus.dot(H.T)

    # 更新步骤
    K = H_t.dot(np.linalg.pinv(H.dot(H_t) + R))  # 卡尔曼增益

    x_hat = x_hat_minus + K.dot(Z.T[i].T - H.dot(x_hat_minus))  # 更新的状态估计
    P = (np.eye(xr) - K.dot(H)).dot(P_minus)  # 更新的误差协方差
    
    for j in range(xr):
        X_mat[j, i] = x_hat[j, 0]
    
    kf = H * X_mat    

# 预测下一日的用电量
predicted_measurement = F.dot(x_hat) + B.dot(u)

print("预测结果:", predicted_measurement[0,0])

plt.rcParams['font.sans-serif'] = ['SimHei']    # 设置正常显示中文
plt.plot(t,np.array(kf)[0], "g", label='预测优化值')
plt.plot(t,np.array(x[0])[0], "r--", label='测量值')
plt.xlabel("时间")                               # 设置X轴的名字
plt.ylabel("位移")                               # 设置Y轴的名字
plt.title("卡尔曼滤波示意图")                     # 设置标题
plt.legend()                                     # 设置图例
plt.show()                                       # 显示图表

预测结果图。
在这里插入图片描述

4.2. 用电量短期预测

我们假设日用电量是一个具有分段线性动态的系统,并且我们拥有每天的用电量实际测量数据。我们的目标是跟踪每日用电量,利用卡尔曼滤波预测未来一天的用电量。

接下来是ChatGPT帮助修改代码,其中过程省略,保留点点滴滴。

在卡尔曼滤波算法中,用于预测下一时刻状态的转移矩阵 F F F 和输入控制矩阵 B B B 需要根据具体的系统模型进行设置。在您提供的代码中,您将 F F F 设置为如下值:

F = np.mat([[1, delta_t], [0, 1]])          # 定义状态转移矩阵

这个设置假设状态向量的第一个分量表示位置,第二个分量表示速度。但在用电量的预测中,这个假设并不合适,因此导致了预测值很大的结果。

为了适应具体的用电量预测问题,您可以根据实际情况进行适当的调整。例如,如果您认为用电量是一个连续增加的过程,您可以将转移矩阵 F 设置为如下值:

F = np.array([[1.]])

这样就假设用电量只与前一天的用电量相关,没有速度的变化。

同样地,输入控制矩阵 B 也需要根据具体情况进行设置。在您的情况下,可以将其设置为零向量,因为您并没有外部输入。

import numpy as np

# 定义系统模型和测量噪声的协方差矩阵
Q = 1e-5  # 系统过程噪声的协方差
R = 0.01  # 测量噪声的协方差
R = np.array([R])

# 初始化状态估计和误差协方差
x_hat = np.array([1.])  # 初始状态估计,将6设置为初始值
P = np.array([[1.]])  # 初始误差协方差
H = np.array([[1.]])  # 定义观测矩阵

# 模拟每天的用电量测量数据
measurements = [6.1, 6.2, 6.3, 6.2, 6.1, 6.0, 5.9, 6.1,6.3,6.5,6.7,6.6,6.5,6.4,6.3,6.2,6.1]  # 用电量测量数据

# 分段线性动态模型参数
F = np.array([[1.]])  # 状态转移矩阵
B = np.array([[0.]])  # 输入控制矩阵
u = 1  # 外部输入

# 卡尔曼滤波
for z in measurements:
    # 预测步骤
    x_hat_minus = F.dot(x_hat) + B.dot(u)  # 预测的状态估计
    print(x_hat_minus)
    P_minus = F.dot(P).dot(F.T) + Q  # 预测的误差协方差
    H_t = P_minus.dot(H.T)

    # 更新步骤
    K = H_t.dot(np.linalg.pinv(H.dot(H_t) + R))  # 卡尔曼增益

    x_hat = x_hat_minus + K.dot(z - H.dot(x_hat_minus))  # 更新的状态估计
    P = (np.eye(1) - K.dot(H)).dot(P_minus)  # 更新的误差协方差

# 预测下一日的用电量
predicted_measurement = F.dot(x_hat) + B.dot(u)

print("预测下一日的用电量:", predicted_measurement[0])

预测下一日的用电量: [6.26423647]。

5. 小结

卡尔曼滤波算法是一种递归的、最优的估计算法,用于估计系统的状态变量。它在处理线性系统和高斯噪声下表现出色,具有良好的估计性能和收敛特性。

  • Python代码实现效果:我们成功地使用NumPy库实现了卡尔曼滤波算法来预测用电量。通过扩展系统模型,包括位置和速度,我们可以对电量的变化进行更精确的估计和预测。

  • 难易度:实现基本的卡尔曼滤波算法并进行预测的难度适中。需要对卡尔曼滤波算法有一定的理解,并具备一定的线性代数和矩阵运算的知识。了解系统模型和噪声特性对算法的实现和调整具有帮助。

  • 扩展:卡尔曼滤波算法具有很强的扩展性。通过扩展系统模型,我们可以处理更复杂的问题,如位置和速度的预测。此外,还可以考虑非线性系统的扩展卡尔曼滤波(EKF)或无迹卡尔曼滤波(UKF),以适应非线性系统模型。

  • 实际应用:卡尔曼滤波在实际应用中广泛用于目标跟踪、导航系统、信号处理等领域。在本例中,卡尔曼滤波算法可以用于预测用电量,在能源管理、智能电网等领域具有重要意义。通过对历史测量数据的处理和状态估计,可以提供准确的用电量预测结果,为用电计划和能源调度提供参考。

ChatGPT在这次工作中发挥了助手的作用,通过理解用户问题并提供相关知识和指导,帮助解释卡尔曼滤波算法的原理和步骤,给出代码实现的建议和提示,提高工作效率和准确性。它还可以提供扩展卡尔曼滤波等更高级的变体算法的信息,帮助用户进一步探索和应用卡尔曼滤波在运动预测中的潜力。

参考:

卡尔曼滤波. MBA智库. 百科

水木清扬. 卡尔曼滤波的原理(Python实现). 博客园. 2022.2

陈军. 陶巍. 吕英飞. 何建平. 基于卡尔曼滤波的短期负荷预测. 电气开关 . 2014.2

一大盘洋芋.卡尔曼滤波算法包(python). CSDN博客. 2023.06

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐