卡尔曼滤波的理解以及参数调整、参数的意义、维度

一、前言

卡尔曼滤波器是一种最优线性状态估计方法(等价于“在最小均方误差准则下的最佳线性滤波器”),所谓状态估计就是通过数学方法寻求与观测数据最佳拟合的状态向量。

在移动机器人导航方面,卡尔曼滤波是最常用的状态估计方法。直观上来讲,卡尔曼滤波器在这里起了数据融合的作用,只需要输入当前的测量值(多个传感器数据)和上一个周期的估计值就能估计当前的状态,这个估计出来的当前状态综合考量了传感器数据(即所谓的观察值、测量值)和上一状态的数据,为当前最优估计,可以认为这个估计出来的值是最可靠的值。由于我们在SLAM中主要用它做位置估计,所以前面所谓的估计值就是估计位置坐标了,而输入的传感器数据包括码盘推算的位置、陀螺仪的角速度等(当然可以有多个陀螺仪和码盘),最后输出的最优估计用来作为机器人的当前位置被导航算法以外的其他程序所调用。

列举一下卡尔曼滤波的优点:采用递归方法解决线性滤波问题,只需要当前的测量值和前一个采样周期的估计值就能够进行状态估计,不需要大量的存储空间,每一步的计算量小,计算步骤清晰,非常适合计算机处理。

二、问题描述与定义

1、前提

首先明确卡尔曼滤波器的前提假设:

  • 信息过程的足够精确的模型,是由白噪声所激发的线性、离散和有限维动态系统(可以是时变的);
  • 每次测量信号都包含着附加的白噪声分量。

满足上述条件就可以使用卡尔曼滤波器。

2、问题描述

定义一个随机离散时间过程的状态向量 ,该过程用一个离散随机差分方程描述: 

xk=Axk1+Buk1+wk1

其中n维向量  xk 为k时刻的系统状态变量,n维向量 xk1  是k-1时刻的系统状态变量。A是状态转移矩阵或者过程增益矩阵,是 n×n  阶方阵,它将k-1时刻状态和当前的k时刻状态联系起来。B是可选的控制输入  uRl 的增益,在大多数实际情况下并没有控制增益,所以  Buk1 这一项很愉快的变成零了。  wk1 是n维向量,代表过程激励噪声,它对应了 xk  中每个分量的噪声,是期望为0,协方差为Q的高斯白噪声, wk ~ N(0,Q)  。 
再定义一个观测变量 ,得到观测方程: 
zk=Hxk+vk

其中观测值 zk 是m阶向量,状态变量 xk  是n阶向量。H是 m×n  阶矩阵,代表状态变量 xk  对测量变量  zk 的增益。观测噪声 vk  是期望为0,协方差为R的高斯白噪声, vk ~ N(0,R)  。

3、举例

如果对上面两个公式不太明白,我举一个例子说明: 
在目标跟踪的应用中,假设质点坐标为  (x,y) 是直接观测得到的,质点在x、y轴方向速度分别为 vx  、 vy ,那么系统状态变量  xk=(x,y,vx,vy)T ,系统观测变量  zk=(x¯,y¯)T  ,系统没有控制输入,所以状态方程就成了: 

xk=Axk1+wk1

它的状态转移矩阵A根据运动学公式确定: 
A=10000100Δt0100Δt01

其实用矩阵的形式写开上面的方程就很好理解了: 
xk=Axk1+wk1xyvxvyk=10000100Δt0100Δt01xyvxvyk1+wk1=x+Δtvxy+Δtvyvxvyk1+wk1

  • 这里简要说一下状态转移矩阵A的确定: 
    (1) 在标量卡尔曼滤波中,比如测量值是温度、湿度,一般认为下一个时刻该温度或者湿度维持不变,这种情况下状态转移矩阵通常就是标量1。 
    (2) 在导航和目标跟踪中卡尔曼滤波常被用来做位置估计,比如匀速直线运动(CV)和匀加速直线运动(CA): 
    ACV=100T10001,ACA=100T10T22T1

    在前面的举例中x,y方向上的运动就被微分近似为匀速直线运动。 
    (3) 若被估计的过程或观测变量与过程的关系是非线性的,此时不能够直接应用卡尔曼滤波(因为不满足上一节提到的卡尔曼滤波适用的前提必须是线性系统),这个时候扩展卡尔曼滤波(EKF)应运而生,它用雅克比矩阵将期望和方差线性化,从而将卡尔曼滤波扩展到非线性系统,但是EKF由于考虑了泰勒级数的展开,运算量大大增加。

该过程的观测方程为 

zk=Hxk+vk

它的观测矩阵H也是要指定的,它的目的是将m维的测量值转换到n维与状态变量相对应,由于直接观测的量是位置 zk=(x¯,y¯)T  ,我们只需要取状态变量的前两个元素就够了,所以H设计成如下: 
H=[10010000]

用矩阵形式写开就是 
zk=Hxk+vk[x¯y¯]k=[10010000]xyvxvy+vk=[xy]k+vk

  • 我再举一个简单例子: 
    一个运动目标的状态变量 xk=(x(k),x˙(k),x¨(k))T  ,其中第一项是目标在k时刻的位置,这里假设为一维坐标,第二项为k时刻的速度,第三项为k时刻的加速度,雷达仅能观测到目标的位置即 x(k)  ,那么它的状态转移矩阵就该设计成: 
    A=100Δt10Δt22Δt1

    它的观测矩阵就该设计成: 
    H=[100]

    到了这里怎么设计矩阵A和H应该有思路了吧。

三、算法流程

接下来就开始介绍卡尔曼滤波最核心的五个更新方程了

1、预测:

x^k¯=Ax^k1+Buk1

P^k¯=AP^k1AT+Q

2、更新:

x^k=x^k¯+Kk(zkHx^k¯)

Kk=P^k¯HTHP^k¯HT+R

P^k=(IKkH)P^k¯

先来解释一下公式中各个变量的含义: 
x^k¯ :表示k时刻先验状态估计值,这是算法根据前次迭代结果(就是上一次循环的后验估计值)做出的不可靠估计。

x^k x^k1 :分别表示k时刻、k-1时刻后验状态估计值,也就是要输出的该时刻最优估计值,这个值是卡尔曼滤波的结果。

A :表示状态转移矩阵,是 n×n  阶方阵,它是算法对状态变量进行预测的依据,状态转移矩阵如果不符合目标模型有可能导致滤波发散,它的确定请参看第二节中的举例。

B :表示可选的控制输入  uRl 的增益,在大多数实际情况下并没有控制增益。

uk1 :表示k-1时刻的控制增益,一般没有这个变量,可以设为0。

P^k¯ :表示k时刻的先验估计协方差,这个协方差矩阵只要确定了一开始的 P^0  ,后面都可以递推出来,而且初始协方差矩阵 P^0  只要不是为0,它的取值对滤波效果影响很小,都能很快收敛。

P^k P^k1 :分别表示k时刻、k-1时刻的后验估计协方差,是滤波结果之一。

Q :表示过程激励噪声的协方差,它是状态转移矩阵与实际过程之间的误差。这个矩阵是卡尔曼滤波中比较难确定的一个量,一般有两种思路:一是在某些稳定的过程可以假定它是固定的矩阵,通过寻找最优的Q值使滤波器获得更好的性能,这是调整滤波器参数的主要手段,Q一般是对角阵,且对角线上的值很小,便于快速收敛;二是在自适应卡尔曼滤波(AKF)中Q矩阵是随时间变化的。

Kk :表示卡尔曼增益,是滤波的中间结果。

zk :表示测量值,是m阶向量。

H :表示量测矩阵,是 m×n  阶矩阵,它把m维测量值转换到n维与状态变量相对应。

R :表示测量噪声协方差,它是一个数值,这是和仪器相关的一个特性,作为已知条件输入滤波器。需要注意的是这个值过大过小都会使滤波效果变差,且R取值越小收敛越快,所以可以通过实验手段寻找合适的R值再利用它进行真实的滤波。

四、算法实现

以下是一段卡尔曼滤波与平滑滤波的对

clear
clc;
N=300;
CON = 25;%房间温度,假定温度是恒定的
%%%%%%%%%%%%%%%卡尔曼滤波%%%%%%%%%%%%%%%%%%%%%%
x = zeros(1,N);
y = 2^0.5 * randn(1,N) + CON;%加过程噪声的测量值

x(1) = 1;
p = 10;

Q = cov(randn(1,N));%过程噪声协方差
R = cov(randn(1,N));%观测噪声协方差
for k = 2 : N
x(k) = x(k - 1);%预估计k时刻状态变量的值
p = p + Q;%对应于预估值的协方差,由于状态变量是标量,状态转移矩阵A为数值1
kg = p / (p + R);%kalman gain
x(k) = x(k) + kg * (y(k) - x(k));
p = (1 - kg) * p;
end


%%%%%%%%%%%平滑滤波%%%%%%%%%%%%%%%%%%%%%%%%

Filter_Wid = 10;
smooth_res = zeros(1,N);
for i = Filter_Wid + 1 : N
tempsum = 0;
for j = i - Filter_Wid : i - 1
tempsum = tempsum + y(j);
end
smooth_res(i) = tempsum / Filter_Wid;
end
% figure(1);
% hist(y);
t=1:N;
figure(1);
expValue = zeros(1,N);
for i = 1: N
expValue(i) = CON;
end
plot(t,expValue,'r',t,x,'g',t,y,'b',t,smooth_res,'k');
legend('real temperature','kalman result','measured value','smooth result');
axis([0 N 20 30])
xlabel('Sample Time');
ylabel('Room Temperature');
title('Smooth Filter VS Kalman Filter');
结果如下图: 

这里写图片描述

五、引用

[1] 王学斌, 徐建宏, 张章. 卡尔曼滤波器参数分析与应用方法研究[J]. 计算机应用与软件, 2012, 29(6):212-215.

转载自:http://blog.csdn.net/u013453604/article/details/50301477


还有一篇写的挺好的:

中文版:http://blog.csdn.net/lybaihu/article/details/54943545

英文版:http://www.bzarg.com/p/how-a-kalman-filter-works-in-pictures/

这里还有一个有程序的,有助于理解。

http://blog.csdn.net/gdfsg/article/details/50904811

本来感觉已经理解了,但是看完opencv自带的例程之后又懵逼了。。。

有一段是这样写的

static void help()
{
	printf("\nExamle of c calls to OpenCV's Kalman filter.\n"
		"   Tracking of rotating point.\n"
		"   Rotation speed is constant.\n"
		"   Both state and measurements vectors are 1D (a point angle),\n"
		"   Measurement is the real point angle + gaussian noise.\n"
		"   The real and the estimated points are connected with yellow line segment,\n"
		"   the real and the measured points are connected with red line segment.\n"
		"   (if Kalman filter works correctly,\n"
		"    the yellow segment should be shorter than the red one).\n"
		"\n"
		"   Pressing any key (except ESC) will reset the tracking with a different speed.\n"
		"   Pressing ESC will stop the program.\n"
		);
}

黄线是预测点(estimated points)与真实值得连线,红线是加了噪声的观测值(measured points)与真实值的连线。

我认为预测点和观测值是用于高斯融合求最优估计值的啊,他们两个都有噪声的,为什么去拿两个都有噪声的进行比较呢?而且黄线比红线段就是正常?





这里贴点opencv源码以便参考。

/*
standard Kalman filter (in G. Welch' and G. Bishop's notation):

  x(k)=A*x(k-1)+B*u(k)+w(k)  p(w)~N(0,Q)
  z(k)=H*x(k)+v(k),   p(v)~N(0,R)
*/
typedef struct CvKalman
{
    int MP;                     /* number of measurement vector dimensions */
    int DP;                     /* number of state vector dimensions */
    int CP;                     /* number of control vector dimensions */

    /* backward compatibility fields */
#if 1
    float* PosterState;         /* =state_pre->data.fl */
    float* PriorState;          /* =state_post->data.fl */
    float* DynamMatr;           /* =transition_matrix->data.fl */
    float* MeasurementMatr;     /* =measurement_matrix->data.fl */
    float* MNCovariance;        /* =measurement_noise_cov->data.fl */
    float* PNCovariance;        /* =process_noise_cov->data.fl */
    float* KalmGainMatr;        /* =gain->data.fl */
    float* PriorErrorCovariance;/* =error_cov_pre->data.fl */
    float* PosterErrorCovariance;/* =error_cov_post->data.fl */
    float* Temp1;               /* temp1->data.fl */
    float* Temp2;               /* temp2->data.fl */
#endif

    CvMat* state_pre;           /* predicted state (x'(k)):
                                    x(k)=A*x(k-1)+B*u(k) */
    CvMat* state_post;          /* corrected state (x(k)):
                                    x(k)=x'(k)+K(k)*(z(k)-H*x'(k)) */
    CvMat* transition_matrix;   /* state transition matrix (A) */
    CvMat* control_matrix;      /* control matrix (B)
                                   (it is not used if there is no control)*/
    CvMat* measurement_matrix;  /* measurement matrix (H) */
    CvMat* process_noise_cov;   /* process noise covariance matrix (Q) */
    CvMat* measurement_noise_cov; /* measurement noise covariance matrix (R) */
    CvMat* error_cov_pre;       /* priori error estimate covariance matrix (P'(k)):
                                    P'(k)=A*P(k-1)*At + Q)*/
    CvMat* gain;                /* Kalman gain matrix (K(k)):
                                    K(k)=P'(k)*Ht*inv(H*P'(k)*Ht+R)*/
    CvMat* error_cov_post;      /* posteriori error estimate covariance matrix (P(k)):
                                    P(k)=(I-K(k)*H)*P'(k) */
    CvMat* temp1;               /* temporary matrices */
    CvMat* temp2;
    CvMat* temp3;
    CvMat* temp4;
    CvMat* temp5;
} CvKalman;

/* Creates Kalman filter and sets A, B, Q, R and state to some initial values */
CVAPI(CvKalman*) cvCreateKalman( int dynam_params, int measure_params,
                                 int control_params CV_DEFAULT(0));

/* Releases Kalman filter state */
CVAPI(void)  cvReleaseKalman( CvKalman** kalman);

/* Updates Kalman filter by time (predicts future state of the system) */
CVAPI(const CvMat*)  cvKalmanPredict( CvKalman* kalman,
                                      const CvMat* control CV_DEFAULT(NULL));

/* Updates Kalman filter by measurement
   (corrects state of the system and internal matrices) */
CVAPI(const CvMat*)  cvKalmanCorrect( CvKalman* kalman, const CvMat* measurement );

CV_IMPL const CvMat*
cvKalmanPredict( CvKalman* kalman, const CvMat* control )
{
    if( !kalman )
        CV_Error( CV_StsNullPtr, "" );

    /* update the state */
    /* x'(k) = A*x(k) */
    cvMatMulAdd( kalman->transition_matrix, kalman->state_post, 0, kalman->state_pre );

    if( control && kalman->CP > 0 )
        /* x'(k) = x'(k) + B*u(k) */
        cvMatMulAdd( kalman->control_matrix, control, kalman->state_pre, kalman->state_pre );

    /* update error covariance matrices */
    /* temp1 = A*P(k) */
    cvMatMulAdd( kalman->transition_matrix, kalman->error_cov_post, 0, kalman->temp1 );

    /* P'(k) = temp1*At + Q */
    cvGEMM( kalman->temp1, kalman->transition_matrix, 1, kalman->process_noise_cov, 1,
                     kalman->error_cov_pre, CV_GEMM_B_T );

    /* handle the case when there will be measurement before the next predict */
    cvCopy(kalman->state_pre, kalman->state_post);

    return kalman->state_pre;
}


CV_IMPL const CvMat*
cvKalmanCorrect( CvKalman* kalman, const CvMat* measurement )
{
    if( !kalman || !measurement )
        CV_Error( CV_StsNullPtr, "" );

    /* temp2 = H*P'(k) */
    cvMatMulAdd( kalman->measurement_matrix, kalman->error_cov_pre, 0, kalman->temp2 );
    /* temp3 = temp2*Ht + R */
    cvGEMM( kalman->temp2, kalman->measurement_matrix, 1,
            kalman->measurement_noise_cov, 1, kalman->temp3, CV_GEMM_B_T );

    /* temp4 = inv(temp3)*temp2 = Kt(k) */
    cvSolve( kalman->temp3, kalman->temp2, kalman->temp4, CV_SVD );

    /* K(k) */
    cvTranspose( kalman->temp4, kalman->gain );

    /* temp5 = z(k) - H*x'(k) */
    cvGEMM( kalman->measurement_matrix, kalman->state_pre, -1, measurement, 1, kalman->temp5 );

    /* x(k) = x'(k) + K(k)*temp5 */
    cvMatMulAdd( kalman->gain, kalman->temp5, kalman->state_pre, kalman->state_post );

    /* P(k) = P'(k) - K(k)*temp2 */
    cvGEMM( kalman->gain, kalman->temp2, -1, kalman->error_cov_pre, 1,
                     kalman->error_cov_post, 0 );

    return kalman->state_post;
}



猜你喜欢

转载自blog.csdn.net/flyyufenfei/article/details/79251330
今日推荐