boss:整个卡尔曼滤波器的简单案例——估计机器人位置

⭐️ 卡尔曼滤波

卡尔曼滤波(Kalman Filtering)是一种用于状态估计的强大技术,常用于处理具有随机噪声的系统的状态估计问题。在目标跟踪等应用中,卡尔曼滤波常被用来预测目标的位置和速度等状态变量,并根据观测数据进行状态更新,从而实现对目标轨迹的跟踪。

在目标跟踪中,可能会出现假点(False Positives)的情况,即目标跟踪算法错误地将背景中的噪声或其他物体错误地识别为目标。为了减少假阳性的影响,可以使用卡尔曼滤波器进行假点过滤。下面是卡尔曼滤波的基本原理:

状态模型: 假设目标的状态变量为 x k \mathbf{x}_k xk,包括位置、速度等信息。卡尔曼滤波器会根据系统的动力学模型来预测目标的下一个状态 x ^ k − \hat{\mathbf{x}}_k^- x^k−。通常采用线性动力学模型,形式为 x k = F k x k − 1 + B k u k + w k \mathbf{x}_k = \mathbf{F}k \mathbf{x}{k-1} + \mathbf{B}_k \mathbf{u}_k + \mathbf{w}_k xk=Fkxk−1+Bkuk+wk,其中 F k \mathbf{F}_k Fk 是状态转移矩阵, B k \mathbf{B}_k Bk 是控制输入矩阵, u k \mathbf{u}_k uk 是外部控制输入, w k \mathbf{w}_k wk 是过程噪声(高斯分布)。

观测模型: 卡尔曼滤波器会根据观测数据来更新状态估计值。假设观测数据为 z k \mathbf{z}_k zk,卡尔曼滤波器会根据观测模型 z k = H k x k + v k \mathbf{z}_k = \mathbf{H}_k \mathbf{x}_k + \mathbf{v}_k zk=Hkxk+vk 来估计目标的位置。其中 H k \mathbf{H}_k Hk 是观测矩阵, v k \mathbf{v}_k vk 是观测噪声(高斯分布)。

卡尔曼滤波器更新: 卡尔曼滤波器利用预测值和观测值的差异,结合系统的动力学模型和观测模型,计算出最优的状态估计值。具体来说,卡尔曼滤波器包括两个主要步骤:

预测步骤(Predict): 根据系统的动力学模型,预测目标的状态变量,并计算预测的状态协方差矩阵 P k − \mathbf{P}_k^- Pk−。

更新步骤(Update): 根据观测模型,计算卡尔曼增益 K k \mathbf{K}_k Kk,并利用观测数据对预测值进行修正,得到最终的状态估计值 x ^ k \hat{\mathbf{x}}_k x^k 和状态协方差矩阵 P k \mathbf{P}_k Pk。

整体过程如下图所示:

卡尔曼的相关资料很多,更详细的知识,读者可以自行搜索,嘻嘻!!!

小编推荐一部著作《Kalman_and_Bayesian_Filters_in_Python》和一位大神的博客 【滤波】设计卡尔曼滤波器,这位大神对前面的著作做了整体的翻译,很牛!!!

⭐️ 设计个卡尔曼滤波器,估计机器人的位置

本文参考《Kalman_and_Bayesian_Filters_in_Python》中的案例,利用函数模拟机器人的位置,然后通过简单卡尔曼滤波器利用测量数据对机器人位置进行估计。

滤波器各个参数设计如下

状态:

状态转移方程:

状态转移矩阵:

观测矩阵:

初始测量误差协方差矩阵:

初始状态及其协方差矩阵:

过程误差协方差矩阵利用白噪声获取。

整体代码如下

python 复制代码
from numpy.random import randn
import matplotlib.pyplot as plt
import numpy as np
from filterpy.kalman import KalmanFilter
from scipy.linalg import block_diag
from filterpy.common import Q_discrete_white_noise
from filterpy.stats import plot_covariance_ellipse


# 模拟传感器,返回robot的位置信息
class PosSensor(object):
    def __init__(self, pos=(0, 0), vel=(0, 0), noise_std=1.):
        self.vel = vel
        self.noise_std = noise_std
        self.pos = [pos[0], pos[1]]
        
    def read(self):
        self.pos[0] += self.vel[0]
        self.pos[1] += self.vel[1]
        
        return [self.pos[0] + randn() * self.noise_std,
                self.pos[1] + randn() * self.noise_std]

# 绘制过滤器输出数据
def plot_filter(xs, ys=None, dt=None, c='C0', label='Filter', var=None, **kwargs):
    if ys is None and dt is not None:
        ys = xs
        xs = np.arange(0, len(ys) * dt, dt)
    if ys is None:
        ys = xs
        xs = range(len(ys))

    lines = plt.plot(xs, ys, color=c, label=label, **kwargs)
    if var is None:
        return lines

    var = np.asarray(var)
    std = np.sqrt(var)
    std_top = ys+std
    std_btm = ys-std

    plt.plot(xs, ys+std, linestyle=':', color='k', lw=2)
    plt.plot(xs, ys-std, linestyle=':', color='k', lw=2)
    plt.fill_between(xs, std_btm, std_top, facecolor='yellow', alpha=0.2)

    return lines

# 测量误差方差
R_std = 0.35
# 过程误差方差
Q_std = 0.04

def tracker():
    # 创建卡尔曼滤波器
    tracker = KalmanFilter(dim_x=4, dim_z=2)
    # 时间步伐
    dt = 1.0
    # 状态转移矩阵
    tracker.F = np.array([[1, dt, 0,  0],
                          [0,  1, 0,  0],
                          [0,  0, 1, dt],
                          [0,  0, 0,  1]])
    # 过程输入值
    tracker.u = 0.
    # 观测矩阵
    tracker.H = np.array([[1/0.3048, 0, 0, 0],
                          [0, 0, 1/0.3048, 0]])
    # 测量误差协方差矩阵
    tracker.R = np.eye(2) * R_std**2
    # 白噪声
    q = Q_discrete_white_noise(dim=2, dt=dt, var=Q_std**2)
    # 过程噪声协方差矩阵
    tracker.Q = block_diag(q, q)
    # 初始状态
    tracker.x = np.array([[0, 0, 0, 0]]).T
    # 初始状态协方差矩阵
    tracker.P = np.eye(4) * 500.
    return tracker

# 模拟机器人移动
N = 30
sensor = PosSensor((0, 0), (2, .5), noise_std=R_std)
# 测量数据,位置信息
zs = np.array([sensor.read() for _ in range(N)])

# 运行过滤器
robot_tracker = tracker()
# 卡尔曼滤波结果
mu, cov, _, _ = robot_tracker.batch_filter(zs)

for x, P in zip(mu, cov):
    # x 和 y 的协方差
    cov = np.array([[P[0, 0], P[2, 0]], 
                    [P[0, 2], P[2, 2]]])
    mean = (x[0, 0], x[2, 0])
    # 绘制卡尔曼滤波器输出的位置信息,包括位置和方差
    plot_covariance_ellipse(mean, cov=cov, fc='g', std=3, alpha=0.5)
    
# 坐标位置转换为单位米
zs *= .3048
# 绘制测量数据和过滤器输出
plot_filter(mu[:, 0], mu[:, 2])
plt.scatter(zs[:, 0], zs[:, 1], color='k', facecolor='none', lw=2, label='Measurements'),
plt.legend(loc=2)
plt.xlim(0, 20)
plt.show()

运行结果如下

笔者水平有限,若有不对的地方欢迎评论指正!

相关推荐
海棠AI实验室15 分钟前
AI的进阶之路:从机器学习到深度学习的演变(一)
人工智能·深度学习·机器学习
hunteritself17 分钟前
AI Weekly『12月16-22日』:OpenAI公布o3,谷歌发布首个推理模型,GitHub Copilot免费版上线!
人工智能·gpt·chatgpt·github·openai·copilot
XH华17 分钟前
初识C语言之二维数组(下)
c语言·算法
南宫生39 分钟前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
不想当程序猿_1 小时前
【蓝桥杯每日一题】求和——前缀和
算法·前缀和·蓝桥杯
IT古董1 小时前
【机器学习】机器学习的基本分类-强化学习-策略梯度(Policy Gradient,PG)
人工智能·机器学习·分类
落魄君子1 小时前
GA-BP分类-遗传算法(Genetic Algorithm)和反向传播算法(Backpropagation)
算法·分类·数据挖掘
centurysee1 小时前
【最佳实践】Anthropic:Agentic系统实践案例
人工智能
mahuifa1 小时前
混合开发环境---使用编程AI辅助开发Qt
人工智能·vscode·qt·qtcreator·编程ai
四口鲸鱼爱吃盐1 小时前
Pytorch | 从零构建GoogleNet对CIFAR10进行分类
人工智能·pytorch·分类