【滤波】平滑

news2024/12/24 9:01:07
%matplotlib inline
#format the book
import book_format
book_format.set_style()

简介

当你考虑未来的数据时,卡尔曼滤波器的性能并不是最优的。例如,假设我们在跟踪飞行器,最新的观测值突然偏离的很离谱,就像这样(我只考虑简单的1维):

import matplotlib.pyplot as plt

data = [10.1, 10.2, 9.8, 10.1, 10.2, 10.3, 
        10.1, 9.9, 10.2, 10.0, 9.9, 11.4]
plt.plot(data)
plt.xlabel('time')
plt.ylabel('position')

在这里插入图片描述

在接近稳定状态的一段时间后,观测值突然有一个很大的变化。假设该变化已经超过了飞机飞行的极限。尽管如此,卡尔曼滤波器还是会根据当前的卡尔曼增益,将新的观测值合并到滤波器中。它不能拒绝噪声,因为观测的结果可能反映转弯的开始。当然,飞机也不太可能如此突然地转向,但不可否认,以下的情况都是可能的:

  • 飞机之前就开始转弯,但之前的观测值结果很嘈杂,没有显示变化;
  • 飞机正在转弯,观测值比较嘈杂;
  • 飞机没有转弯,观测值非常嘈杂;
  • 飞机正在相反方向转弯,观测值比较嘈杂;

现在,假设接下去的观测值为:

data2 = [11.3, 12.1, 13.3, 13.9, 14.5, 15.2]
plt.plot(data + data2)

在这里插入图片描述

根据这些未来的观测结果,我们可以推断出:飞机开始转弯。

另一方面,假设接下去的观测值为:

data3 = [9.8, 10.2, 9.9, 10.1, 10.0, 10.3, 9.9, 10.1]
plt.plot(data + data3)

在这里插入图片描述

在这种情况下,我们得出结论:飞机没有转弯,只是因为观测值非常嘈杂。


平滑器工作原理概述

卡尔曼滤波器是一种具有马尔可夫特性的递归滤波器——它在步骤 k k k的估计仅基于步骤 k − 1 k-1 k1的估计和步骤 k k k的观测。但这意味着步骤 k − 1 k-1 k1的估计基于步骤 k − 2 k-2 k2,依此类推回到初始值。因此,第 k k k步的估计取决于之前的所有观测,尽管程度不同。 k − 1 k-1 k1的影响力最大, k − 2 k-2 k2次之,以此类推

平滑器将未来的观测值也纳入步骤 k k k的估计中。来自 k + 1 k+1 k+1的观测值的影响最大, k + 2 k+2 k+2的影响较小, k + 3 k+3 k+3的影响更小,依此类推

当然,我可以通过一个低通滤波器平滑上面的数据。最终结果将是平滑的,但不一定准确,因为低通滤波器将真实的变化也消除了,就像它消除噪声一样。相比之下,卡尔曼平滑器是最优的——它们结合了所有可用信息,以做出数学上可以实现的最佳估计。

平滑器的类型

在这些情况下,有三种卡尔曼平滑器可以产生更好的效果:

固定区间平滑

这是一个基于批处理的平滑器。此平滑器在进行任何估计之前,会等待收集所有数据。例如,你可能是一名为实验收集数据的科学家,在实验完成之前不需要知道结果。固定区间平滑器将收集所有数据,然后使用所有可用的历史的和未来的观测值来估计每次观测的状态。如果你可以在批处理模式下运行卡尔曼平滑器,则始终建议使用这类平滑器,它将提供比之前章节中递归形式的滤波器更好的结果。

固定延迟平滑

固定延迟平滑器将延迟引入到输出。假设我们选择4步的延迟,平滑器将接收前3个观测值,但不输出滤波结果。然后,当第4个观测值进入时,滤波器将会考虑第1到4个的观测值,同时产生第1个观测值的输出。当第5个观测值进入时,将会考虑第2到5个的观测值,同时产生第2个观测值的输出。但是,如果你需要最新的数据,可能就需要滞后了。

固定点平滑

固定点平滑器,与普通的卡尔曼滤波器一样工作,但只会在某个固定时间点 j j j处产生状态估计。在时间 k k k达到 j j j之前,平滑器和常规滤波器一样工作。一旦 k > j k>j k>j,平滑器仍会继续估计 x k x_{k} xk,同时也会使用 j . . . k j...k j...k之间的观测对 x j x_{j} xj进行更新。这有助于估计系统的初始参数,或对特定时间发生的事件产生最佳估计。例如,你可能有一个机器人在时间 j j j拍摄了一张照片。当机器人继续移动时,你可以使用固定点平滑器为时间 j j j获取尽可能最佳的机器人姿势信息。

平滑器的选择

这些平滑器的选择取决于你的需求,以及你可以提供多少内存和计算能力。

固定区间平滑以批处理为代价产生最平滑的输出。大多数算法都使用某种向前/向后的算法,其速度只比递归卡尔曼滤波器慢一倍。

固定延迟平滑只需要存储一个数据窗口,并且处理能力要求不高,因为每次新观测只处理该窗口。缺点是平滑器的输出总是滞后于输入,并且平滑效果不如固定区间平滑效果明显。


固定区间平滑

文献中有许多固定区间平滑器可用。我选择实现Rauch、Striebel和Tung发明的平滑器,因为它易于实现,计算效率高。它也是我见过的在实际应用中最常用的平滑方法,这种平滑器通常被称为RTS平滑器

RTS平滑器的推导过程涉及数页密集的数学。我不会把它强加给你的,取而代之的是,我将简要介绍算法、方程,然后直接介绍平滑器的实现和演示。

RTS平滑器首先以批处理模式运行卡尔曼滤波器,计算每个步骤的滤波器输出。给定了每个观测的滤波器输出以及对应于每个输出的协方差矩阵之后,RTS向后运行数据,将对未来的信息融入到过去的观测中。当它到达第一个观测值时,整个过程就完成了,最终的输出以最大的最佳形式包含了所有的信息

RTS平滑器的方程非常简单,易于实现。此推导适用于线性卡尔曼滤波器,EKF和UKF也存在类似的推导。这些步骤是在批处理的输出上执行的,从最近的时间倒退到第一次估计。每次迭代都将未来的信息纳入状态估计中。由于状态估计已经包含了所有过去的观测值,因此每个估计值将包含过去和未来所有观测值的知识。区分过去、现在和未来非常重要,因此我使用了下标来表示数据的时刻。后向运行数据的步骤为:

预测步:

P = F P k F T + Q \mathbf{P=FP}_{k}\mathbf{F}^{T}+\mathbf{Q} P=FPkFT+Q

更新步:

K k = P k F T P − 1 \mathbf{K}_{k}=\mathbf{P}_{k}\mathbf{F}^{T}\mathbf{P}^{-1} Kk=PkFTP1

x k = x k + K k ( x k + 1 − F x k ) \mathbf{x}_{k}=\mathbf{x}_{k} + \mathbf{K}_{k}(\mathbf{x}_{k+1}-\mathbf{F}\mathbf{x}_{k}) xk=xk+Kk(xk+1Fxk)

P k = P k + K k ( P k + 1 − P ) K k T \mathbf{P}_{k}=\mathbf{P}_{k}+\mathbf{K}_{k}(\mathbf{P}_{k+1}-\mathbf{P})\mathbf{K}_{k}^{T} Pk=Pk+Kk(Pk+1P)KkT

和往常一样,实现中最困难的部分是正确地计算下标。一个基本实现是:

def rts_smoother(Xs, Ps, F, Q):
    n, dim_x, _ = Xs.shape

    # smoother gain
    K = zeros((n,dim_x, dim_x))
    x, P, Pp = Xs.copy(), Ps.copy(), Ps.copy

    for k in range(n-2,-1,-1):
        Pp[k] = F @ P[k] @ F.T + Q # predicted covariance

        K[k]  = P[k] @ F.T @inv(Pp[k])
        x[k] += K[k] @ (x[k+1] - (F @ x[k]))     
        P[k] += K[k] @ (P[k+1] - Pp[k]) @ K[k].T
    return (x, P, K, Pp)

此实现和FilterPy中提供的实现很类似。它假设卡尔曼滤波器以批处理模式在外部运行,状态和协方差的结果通过XsPs变量传入。

下面是一个例子:

import numpy as np
from numpy import random
from numpy.random import randn
import matplotlib.pyplot as plt
from filterpy.kalman import KalmanFilter
from filterpy.common import Q_discrete_white_noise
import kf_book.book_plots as bp

def plot_rts(noise, Q=0.001, show_velocity=False):
    random.seed(123)
    fk = KalmanFilter(dim_x=2, dim_z=1)

    fk.x = np.array([0., 1.])      # state (x and dx)

    fk.F = np.array([[1., 1.],
                     [0., 1.]])    # state transition matrix

    fk.H = np.array([[1., 0.]])    # Measurement function
    fk.P*= 10.                     # covariance matrix
    fk.R = noise                   # state uncertainty
    fk.Q = Q_discrete_white_noise(dim=2, dt=1., var=Q)  # process uncertainty

    # create noisy data
    zs = np.asarray([t + randn()*noise for t in range (40)])

    # filter data with Kalman filter, than run smoother on it
    mu, cov, _, _ = fk.batch_filter(zs)
    M, P, C, _ = fk.rts_smoother(mu, cov)

    # plot data
    if show_velocity:
        index = 1
        print('gu')
    else:
        index = 0
    if not show_velocity:
        bp.plot_measurements(zs, lw=1)
    plt.plot(M[:, index], c='b', label='RTS')
    plt.plot(mu[:, index], c='g', ls='--', label='KF output')
    if not show_velocity:
        N = len(zs)
        plt.plot([0, N], [0, N], 'k', lw=2, label='track') 
    plt.legend(loc=4)
    plt.show()
    
plot_rts(7.)

在这里插入图片描述

我已经在信号中注入了大量的噪声,让你可以从视觉上区分RTS输出和KF滤波器输出。在上图中,我们可以看到,与输入相比,绘制为绿色虚线的卡尔曼滤波器比较平滑。但是,当连续几次观测值都发生偏离时,滤波器也发生了偏离。相比之下,RTS输出非常平滑,非常接近理想值。

通过一个可能更合理的噪声量,我们可以看到RTS输出几乎位于理想值上。相比较于观测值,卡尔曼滤波器的输出虽然好得多,但变化幅度仍然较大。

plot_rts(noise=1.)

在这里插入图片描述

然而,我们必须理解,这种平滑是基于系统模型的。我们告诉滤波器,我们跟踪的是一个恒速模型,过程误差非常小。当滤波器发现未来的行为与恒定的速度非常匹配时,它就能够抑制信号中的大部分噪声。相反,假设我们的系统有很多过程噪声。例如,如果我们在狂风中跟踪轻型飞机,其速度将经常变化,并且滤波器将无法区分噪声和风引起的不稳定运动。我们可以在下一张图表中看到这一点。

plot_rts(noise=7., Q=.1)

在这里插入图片描述

平滑器根据之前的观测结果、未来的观测结果,以及你告诉它的关于系统行为、系统和观测中噪声的信息,做出最佳估计。

让我们通过观察卡尔曼滤波器和RTS平滑器的速度估计来总结一下:

plot_rts(7.,show_velocity=True)

在这里插入图片描述

作为一个隐藏量,速度的改善更为显著。


固定延迟平滑

如果可以在批处理模式下运行,则上述RTS平滑器应始终是你的首选,因为它将所有的可用数据合并到每个估计中。然而,并非所有的问题都允许你这样做,但你仍然想要获取先前估计的平滑值。下面的数字行说明了这个概念。

from kf_book.book_plots import figsize
from kf_book.smoothing_internal import *

with figsize(y=2):
    show_fixed_lag_numberline()

在这里插入图片描述

在步骤 k k k中,我们可以使用标准卡尔曼滤波方程估计 x k x_{k} xk。然而,我们可以使用 x k x_{k} xk收到的观测值,更好地估计 x k − 1 x_{k-1} xk1。同样,我们可以使用 x k − 1 x_{k−1} xk1 x k x_{k} xk收到的观测值,更好地估计 x k − 2 x_{k−2} xk2。我们可以将这种计算向后扩展到任意N步。

数学推导超出了本文的范围。如果你感兴趣的话,Dan Simon的《最优状态估计》中有一个非常好的解释。这个想法的本质是,我们不需要状态向量 x \mathbf{x} x,而是建立一个增强状态量:

x = [ x k x k − 1 . . . x k − N + 1 ] \mathbf{x}=\begin{bmatrix}\mathbf{x}_{k} \\ \mathbf{x}_{k-1} \\ ... \\ \mathbf{x}_{k-N+1} \end{bmatrix} x= xkxk1...xkN+1

这将产生一个非常大的协方差矩阵,其中包含不同步的状态量的协方差。FilterPy的类FixedLagSmoother为你处理所有的这些计算,包括增广矩阵的创建。你需要做的就是像使用KalmanFilter类一样编写它,然后调用smooth(),它实现了算法的预测和更新步。

每次调用smooth()都会计算当前观测的估计值,同时它也会返回并调整之前的 N − 1 N-1 N1个点。平滑后的值包含在列表FixedLagSmoother.xSmooth。如果使用FixedLagSmoother.x,你将获得最新的估计值,但它不是平滑的,与标准卡尔曼滤波器输出没有区别。

from filterpy.kalman import FixedLagSmoother, KalmanFilter
import numpy.random as random

fls = FixedLagSmoother(dim_x=2, dim_z=1, N=8)

fls.x = np.array([0., .5])
fls.F = np.array([[1.,1.],
                  [0.,1.]])

fls.H = np.array([[1.,0.]])
fls.P *= 200
fls.R *= 5.
fls.Q *= 0.001

kf = KalmanFilter(dim_x=2, dim_z=1)
kf.x = np.array([0., .5])
kf.F = np.array([[1.,1.],
                 [0.,1.]])
kf.H = np.array([[1.,0.]])
kf.P *= 200
kf.R *= 5.
kf.Q = Q_discrete_white_noise(dim=2, dt=1., var=0.001)

N = 4 # size of lag

nom =  np.array([t/2. for t in range (0, 40)])
zs = np.array([t + random.randn()*5.1 for t in nom])

for z in zs:
    fls.smooth(z)
    
kf_x, _, _, _ = kf.batch_filter(zs)
x_smooth = np.array(fls.xSmooth)[:, 0]


fls_res = abs(x_smooth - nom)
kf_res = abs(kf_x[:, 0] - nom)

plt.plot(zs,'o', alpha=0.5, marker='o', label='zs')
plt.plot(x_smooth, label='FLS')
plt.plot(kf_x[:, 0], label='KF', ls='--')
plt.legend(loc=4)

print(f'standard deviation fixed-lag: {np.mean(fls_res):.3f}')
print(f'standard deviation kalman: {np.mean(kf_res):.3f}')
standard deviation fixed-lag: 2.616
standard deviation kalman: 3.564

在这里插入图片描述

这里我设置了 N = 8 N=8 N=8,这意味着我们将把8个未来的观测值纳入我们的估计。一旦滤波器收敛,这为我们提供了一个非常平滑的估计,代价大约是标准卡尔曼滤波器计算量的8倍。请随意尝试 N N N的大小值。我随机选择了8,不是出于任何理论上的考虑。


相关阅读

  • Kalman-and-Bayesian-Filters-in-Python/blob/master/13-Smoothing
  • 卡尔曼滤波系列——(六)卡尔曼平滑

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/698344.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PHP的pack/unpack

前言:直接参照官网。 PHP: pack - Manual PHP中文手册 PHP中国镜像 php 国内镜像 PHP官方网站 PHP: unpack - Manual PHP中文手册 PHP中国镜像 php 国内镜像 PHP官方网站 1、作用 (1)pack:将数据打包成二进制字符串。将输入数据…

链表刷题(9-11)

目录 相交链表 环形链表 环形链表Ⅱ 相交链表 力扣 第一种思路:判断尾节点地址是否相同,时间复杂度为O(N^2)。 第二种思路:(节点对齐)记录两个链表节点个数,再根据节点差设置两个快慢指针进行next节点比对。时间复杂度O(N)(3N)…

PHP 税务申报征收系统mysql数据库web结构apache计算机软件工程网页wamp

一、源码特点 PHP 税务申报征收系统 是一套完善的WEB设计系统,对理解php编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。 代码下载 https://download.csdn.net/download/qq_41221322/87959340https://downl…

spring boot security之前后端分离配置

前言 spring boot security默认配置有一个登录页面,当采用前后端分离的场景下,需要解决两个问题: 前端有自己的登录页面,不需要使用spring boot security默认的登录页面登录相关接口允许匿名访问 因此需要自定义相关实现。 自…

【C51 --- 单片机学习历程与分享】

51单片机学习历程与分享 开篇 --- 认识单片机1、什么是单片机?2、51单片机主要资源3、STC89C51 芯片简介4、单片机脚位判断5、51单片机的应用领域6、如何学好51单片机?7、参考文献 开篇 — 认识单片机 前言: 1.本专栏适合有一定C语言功底的读…

数据结构与算法:数组和字符串

1 数组 1.1 集合、列表、数组的联系与区别 集合:由一个或多个确定的元素所构成的整体。类型不一定相同、确定、无序、互异。 列表(又称线性列表):按照一定的线性顺序,排列而成的数据项的集合。类型不一定相同、有序…

所有独立站都适合做谷歌推广吗?怎么做好谷歌推广?

大家有没有这种困扰:是不是所有的独立站都适合用谷歌来打广告呢?我的行业能不能用Google Ads来推广?如果我刚刚起步,我应该开启哪种类型的广告呢?让我们一起来揭秘吧! 如果你是一个独立站卖家,…

用异或计算只出现一次的数字

因为与0异或的都是数字本身&#xff0c;数字本身和数字本身异或是等于0&#xff0c;应用这个定理&#xff0c;我们来做这个题 链接: leetcode用异或计算只出现一次的数字 class Solution { public:int singleNumber(vector<int>& nums) {size_t v 0;for(size_t i …

Git指南 - 刚提的commit 怎么找不到了(游离分支)?

在有一次使用git时&#xff0c;我提交commit后&#xff0c;并未push&#xff0c;然后直接切到了当前分支的某个tag&#xff0c;最后我想切回来的时候&#xff0c;竟然找不到我刚才提交commit的节点了… 关联篇 Git指南 - 你该掌握的那些基础认知和首次配置Git指南 - 项目实战中…

uni-app处理请求发送表单类型的数据

我在本地开发了一个分页的接口 这里 我设置的是 form-data 参数类型 要的是个表单类型的数据 然后呢 我按传统PC端的方式处理了数据 <template><view class "box"><view class"management"></view></view> </template…

QT Creator上位机学习(二)基础布局控件及信号与槽

c# 系列文章目录 文章目录 布局控件信号与槽第二种connect 程序图标使用技巧 布局控件 美化界面的时候&#xff0c;经常需要进行一些控件的布局&#xff0c;这时需要使用一些容器类。 在快捷栏出&#xff0c;也有一些布局设计的选择 如上图&#xff0c;其中涉及到几种编辑…

【图像软件篇】Windows最强大的截图贴图神器-Snipaste的优化设置

【图像软件篇】Windows最强大的截图贴图神器-Snipaste的优化设置 个人用户免费、开源&#xff0c;以及和剪贴板神器Ditto一样简单易用&#xff0c;默认设置上手够快&#xff0c;除了没有长截图和OCR功能&#xff0c;我觉得它已经做到了截图贴图软件的天花板&#xff1b;本文我…

Springboot的配置原理

一、起步依赖–Maven的依赖传递 原始基于Spring框架来运行&#xff0c;需要手动配置很多依赖项&#xff0c;而Springboot简化了基于Spring框架的开发–引入Springboot的起步依赖&#xff0c;里面引入了所有常见的Springboot的依赖&#xff0c;都是通过maven的依赖传递自动的传…

Redisson 延时队列 延时严重问题

延时队列原理我在这篇文章讲了 Redisson 延时队列 原理 详解 - 知乎 十分建议先把原理看了 我们一个项目是做消息推送的&#xff0c; 分钟量达到了几百万。需求是要设置5秒以上的延时推消息。 当初我想了几个方案&#xff1a; 定时器轮询数据库 mq做延时推送 redisson做延时推…

【微服务架构模式】微服务设计模式

这是微服务架构系列文章的第 3 篇 高可用性、可扩展性、故障恢复能力和性能是微服务的特征。您可以使用微服务架构模式来构建微服务应用程序&#xff0c;从而降低微服务失败的风险。 模式分为三层&#xff1a; 应用模式 应用程序模式解决了开发人员面临的问题&#xff0c;例如数…

应用面向对象思想进行Linux内核分析的优化方法

在分析Linux内核时&#xff0c;应用面向对象思想可以帮助我们更好地理解和组织内核代码。虽然Linux内核是用C语言编写的&#xff0c;并没有内置的面向对象机制&#xff0c;但我们可以通过一些方法来应用面向对象思想进行分析。 我这里刚好有嵌入式、单片机、plc的资料需要可以…

在线客服系统哪家好,2024五家常用客服系统权威测评

在线客服系统推荐哪家随着互联网的发展&#xff0c;人们沟通交流方式越来越趋向智能化。很多企业都会选择在线客服系统来提升员工效率和质量&#xff0c;而且还能为企业带来一个强大的销售平台。那么在线客服系统推荐哪家呢&#xff1f;首先我们要知道&#xff0c;在线客服系统…

DINO推理模块实现

如何将一个模型真正的投入应用呢&#xff1f;即我们常说的推理模块&#xff0c;前面博主已经介绍了如何使用DETR进行推理&#xff0c;今天博主则介绍DINO的推理实现过程&#xff1a; 其实在DINO的代码中已经给出了推理模块的实现&#xff0c;这里博主是将其流程进行梳理&#x…

w3af启动后提示“Traceback (most recent call last)”

第一次提示 /usr/bin/env: “python”: 没有那个文件或目录 一看这提示就是python目录没有引用对&#xff0c;所以建一个软链接 软连接参考&#xff1a;每天学命令-ln 软硬链接 | 夜云泊个人博客 命令如下 whereis python3 sudo ln -s /usr/bin/python3 /usr/bin/python 软…

OpenCV阈值处理(threshold函数、自适应阈值处理、Otsu处理)

目录 阈值处理 一.threshold函数 1.二值化阈值处理&#xff08;cv2.THRESH_BINARY&#xff09; 2.反二值化阈值处理&#xff08; cv2.THRESH_BINARY_INV&#xff09; 3.截断阈值化处理&#xff08;cv2.THRESH_TRUNC&#xff09; 4.超阈值零处理&#xff08;cv2.THRESH_TOZ…