从MATLAB到Python的错误状态卡尔曼滤波器

6

我正在尝试在Python中复现这里讲解的算法,但是我遇到了一些关于某些参数增长异常的问题。以下是我的尝试。请注意,get_ang()get_acc()分别返回度/秒以及m/s^2的[x,y,z]轴上的角速度和加速度(但是我将数据转换为弧度/秒)。:

import numpy as np
import quaternion
from utils import get_ang, get_acc

#utils
Z=np.zeros([3,3])
I=np.eye(3)
EARTH_GRAVITY_MS2 = -9.80665

#sample parameters
N=1        #DecimationFactor
fs=10      #SampleRate

#noise parameters
beta=3.0462e-13      #GyroscopeDriftNoise
eta=9.1385e-5       #GyroscopeNoise
kappa=N/fs  #DecimationFactor/SampleRate
lamb=0.00019247      #AccelerometerNoise
nu=0.5        #LinearAccelerationDecayFactor
csi=0.0096236       #LinearAccelerationNoise

#other parameters initialization
lin_acc_prior=np.zeros(3)
gyro_offset=np.zeros([1,3])
Q=np.diag([0.000006092348396, 0.000006092348396, 0.000006092348396, 0.000076154354947,0.000076154354947, 0.000076154354947,0.009623610000000, 0.009623610000000, 0.009623610000000])
R=(lamb+csi+(kappa**2)*(beta+eta))*I
P=Q
q=quaternion.quaternion(1,0,0,0)                     


while(1):

    """----------------------------------------------------------Model----------------------------------------------------------"""

    """Predict orientation (q-)"""
    gyro_readings=np.array(np.radians([get_ang()])) #rad/s

    for i in range(N-1):
        gyro_readings=np.append(gyro_readings,np.radians([get_ang()]),axis=0)

    delta_phi=(gyro_readings-gyro_offset)/fs    #rad/s  
    delta_q=quaternion.from_rotation_vector(delta_phi)
    q=q*np.prod(delta_q)

    """Estimate gravity from orientation (g)"""
    r_prior=quat2rotm(q) 
    g=r_prior[:,2:3].transpose()*(-EARTH_GRAVITY_MS2)   #m/s^2

    """Estimate gravity from acceleration (g_acc)"""
    accel_readings=get_acc() #m/s^2
    g_acc=accel_readings-lin_acc_prior #m/s^2


    """----------------------------------------------------------Error Model----------------------------------------------------------"""

    "Error Model (z)"
    z=g-g_acc #m/s^2

    """----------------------------------------------------------Kalman Equations----------------------------------------------------------"""

    """Observation model (H)"""
    gx=g[0,0]
    gy=g[0,1]
    gz=g[0,2]
    g_cross=np.array([[0, gz, -gy],[-gz, 0, gx],[gy, -gx, 0]])
    H=np.block([g_cross, -kappa*g_cross, I])

    """Innovation covariance (S)""" 
    S=R+np.dot(H,np.dot(P,H.transpose()))

    """Kalman gain (K)"""
    K=np.dot(P,np.dot(H.transpose(),np.linalg.inv(S)))

    """Update error estimate covariance (P+)"""
    P=P-np.dot(K,np.dot(H,P))

    """Predict error estimate covariance (P-)"""
    D1=np.diag(np.diag(P[0:3,0:3]))   #first diagonal block P
    D2=np.diag(np.diag(P[3:6,3:6]))   #second diagonal block P
    D3=np.diag(np.diag(P[6:9,6:9]))   #third diagonal block P

    Q11=D1+kappa**2*D2+(beta+eta)*I
    Q12=(D2+beta*I)
    Q12[0,0]*=-kappa
    Q22=D2+beta*I
    Q33=nu**2*D3+csi*I

    Q=np.block([[Q11,Q12,Z],[Q12,Q22,Z],[Z,Z,Q33]])
    P=Q

    """Update posterior error (x)"""
    x=np.dot(K,z.transpose())

    """----------------------------------------------------------Correct----------------------------------------------------------"""

    """Estimate orientation (q+)"""
    theta=x[:3].transpose() #rad
    q=q*quaternion.from_rotation_vector(-theta)[0]




    """Estimate linear acceleration (lin_acc_prior)"""
    b=x[3:6].transpose() #rad/s
    lin_acc_prior = lin_acc_prior*nu-b

    """Estimate gyro offset (gyro_offset)"""
    a=x[6:].transpose()
    gyro_offset=gyro_offset-a

    """----------------------------------------------------------Compute Angular Velocity----------------------------------------------------------"""

    """Angular velocity (angular_velocity)"""
    angular_velocity=np.sum(gyro_readings,axis=0)/N-gyro_offset

当我的IMU保持静止状态(get_ang返回约为[0, 0, 0]的值,而get_acc返回约为[0, 0, -9.8]的值)时,我观察到gyro_offset的异常增长(可能是由于a值不够小所致),导致delta_phidelta_qq和因此gz的估计错误。

我检查了我的代码很多次,但没有找到任何错误。我认为我可能会误解上面链接中的说明,可能会在测量单位(度、弧度、m / s ^ 2、g)方面混淆,但即使尝试不同的组合,我也得到类似的行为。

请帮助我找出我缺少什么。

附:可以在每个步骤中放置:

gyro_readings=np.random.normal(0,1,[1,3])/50 
accel_readings=np.array([0,0,-9.8])+np.random.normal(0,1,[3])/50


1
我查看了参考的PDF文件,发现其中有很多与Matlab页面上描述不符的地方。我将纠正所有这些问题并查看是否有效。 - Anton
非常感谢,希望你能帮助我 :) - aleio1
这个问题非常有趣,我会尽力找到足够的时间来解决它。到目前为止,我已经在Matlab中实现了整个代码,但我观察到与你的情况相同的行为。 - Anton
2个回答

2
在你提供的链接中,卡尔曼方程中S的转置被倒置以计算卡尔曼增益。看起来在倒置之前你没有对S进行转置。在这一行中:
K=np.dot(P,np.dot(H.transpose(),np.linalg.inv(S)))

最初的回答应该是:

它应该是


K=np.dot(P,np.dot(H.transpose(),np.linalg.inv(S.transpose())))

1
谢谢您的回复,但S是对称的,因此它的转置矩阵就是S本身。 - aleio1
我记得我的卡尔曼滤波器存在稳定性问题,有时由于浮点数误差导致S和P的对称性破裂,也许将它们与它们的转置相加并除以二可以解决你的问题。 - Mete Han Kahraman
再次感谢您,但我已经检查过了,我的计算没有对称性损失。因此,即使使用您的技巧,我也观察到相同的行为。 - aleio1

1
我在您的代码中看到以下问题:
  • 在计算方向矩阵中的g时,您将其乘以地球重力。结果,您的观测误差和创新是以m/s2为单位进行测量的。根据文档,滤波器需要以单位 g表示误差。因此,我宁愿将g_acc除以地球重力。

  • 访问状态向量x时,您使用了元素4:6来进行线性加速度估计,但这些元素属于陀螺仪偏移量。对于加速度,元素7:9也是同样的情况,而不是用于陀螺仪偏移量。

  • 生成测试信号时,您使用一些参数来模拟噪声的正态分布。我会使用与过滤器实现中使用的完全相同的噪声参数,否则这两个噪声水平将不相应,并且过滤器无法最优化执行。

  • 在matlab页面上给出的Q公式与文档中的原始公式不符。比较方程10.1.2310.1.24。它们分别涉及P元素[0,2:3,5][3,5:3,5]。在您的情况下,这意味着子矩阵Q12不正确。

很遗憾,我无法运行您的Python代码以检查它是否能更好地使用上述建议。但是,我的Matlab代码在它们的帮助下表现更好。请尝试并发布一些图形?

我正在尝试实现你的建议。不过我想强调一下你最后提到的一点:我认为文档中有一个打字错误。正如你所看到的,在方程式10.1.23中,左边包含Q_bb,其值为P [3,5:3,5](请参见方程式10.1.19),但在右侧被替换为P [0,2:3,5],正如你所指出的那样。我认为这可能是转录时的一个错误。 你能否请检查一下这个问题? - aleio1
我认为你是对的。可能他们的意思是Thetab不相关,它们的协方差可以忽略不计。我想知道为什么文档和Matlab描述存在这么多不匹配之处... - Anton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接