基于卡尔曼滤波算法的轨迹跟踪实例和python代码

卡尔曼滤波算法简介

卡尔曼滤波算法是比较经典的用于轨迹跟踪、轨迹平滑、状态估计的算法。

前提假设

卡尔曼滤波使用的前提之一是该系统是线性的,且维度有限。个人对线性系统的理解为下一时刻系统状态和当前时刻系统状态可以用一个线性关系函数来表达。这个线性关系只存在于前后时刻的系统状态间,长时间可以不成立。比如对于一个车辆轨迹的跟踪,每个时刻车辆的状态定义为[position, velocity]。前后时刻间的时长足够短的话,可以把车辆的运动近似为匀速运动。那么下一个时刻的车辆状态转移方程可以写成:position_next = position_pre + t * velocity_pre,velocity_next = velocity_pre,可以看出都为线性关系。因此可以称之为线性系统。
第二个前提是测量信息中包含的误差都为白噪音,即测量误差分布为高斯分布。一般来说检测器的随机误差都符合高斯分布。

特点

适用于连续变化的系统;
轻量化,只用保存上一状态的信息;
快速,适用于实时问题。

原理

详细的原理推荐大家学习此文章: https://www.bzarg.com/p/how-a-kalman-filter-works-in-pictures/

Python实例

本文构建了一个匀加速直线运动的物体,其中把加速度看成是控制变量。(其实也可以把加速度放入状态变量中进行考虑)模拟了20秒的跟踪,每秒更新一次。
其中有几个协方差矩阵比较绕,稍微解释一下:
1、Q表示外界噪音矩阵。可以理解为状态转移预测值和真实值之间的误差。有可能因为道路颠簸或其它外界原因导致的误差,是不太可知的。根据经验大概给一个靠谱的值就好。若设置Q<<R,则滤波会很信任估计值,若设置Q>>R则滤波器会很信任测量值。
2、R表示测量值和真实值之间的协方差矩阵。表示检测设备的误差,这理论上是可以检测出来的。
3、P表示状态协方差矩阵。表示估计值和真实值之间的误差大小。初始化为单位矩阵就好,会自动更新。
(备注:位置和速度可以看成两个独立变量,因此协方差矩阵只在对角线上有值)

import numpy as np
import matplotlib.pyplot as plt


class KalmanFilter:
    def __init__(self, F, B, u, Q, H, R, P):
        """
        :param F: 状态转移矩阵,固定
        :param B: 控制矩阵,固定
        :param u: 控制向量,例如加速度
        :param Q: 外界噪音,协方差矩阵,固定
        :param H: 单位转化矩阵,固定
        :param R: 观测值的协方差矩阵,固定
        :param P: 状态协方差矩阵,更新变化
        """
        self.F = F
        self.B = B
        self.u = u
        self.Q = Q
        self.H = H
        self.R = R
        # K为卡尔曼增益,基于H、P、R计算,更新变化
        self.K = 0
        self.P = P

    def predict(self, x0):
        x1 = np.dot(self.F, x0) + self.B * self.u
        self.P = np.dot(np.dot(self.F, self.P), self.F.T) + self.Q
        return x1

    def update(self, x1, z):
        P_ = np.dot(np.dot(self.H, self.P), self.H.T)
        self.K = np.dot(P_, np.linalg.inv(P_ + self.R))
        x_ = x1 + np.dot(self.K, (z - np.dot(self.H, x1)))
        # 更新P
        self.P = P_ - np.dot(self.K, np.dot(self.H, P_))
        return x_

if __name__ == "__main__":
    """
    构建初始速度为v0的匀加速运动的一维物体
    加速度为a, 作为控制信息
    状态为位置p和速度v
    用卡尔曼滤波来进行跟踪
    """
    # 总时间
    T = 20
    # 速度和加速度初始化值
    v0 = 0
    a = 1
    # 物体状态真实值
    p = [v0 * t + 0.5 * a * t**2 for t in range(T)]  # 真实位置
    p = np.array(p)
    v = [v0 + a * t for t in range(T)]  # 真实速度
    v = np.array(v)
    # 构建观测值
    # # 模拟白噪音,精确到小数点后两位
    sd_p = 3  # 位置标准差设置为3
    sd_v = 1  # 速度标准差设置为1
    noise_p = np.round(np.random.normal(0, sd_p, T), 2)  # 位置噪音
    noise_v = np.round(np.random.normal(0, sd_v, T), 2)  # 速度噪音
    # # 带有噪音的观测值
    z = np.array([p + noise_p, v + noise_v])
    # 实例化滤波器
    t = 1  # 时间间隔
    n = 2  # 物体状态[p, v]的维度
    F = np.array([[1, t], [0, 1]])  # 状态转移矩阵, [p_1, v_1] = [[1, t], [0, 1]] * [p_0, v_0]
    B = np.array([[0.5 * t**2], [t]])  # 控制矩阵, 描述控制变量和物体状态之间的关系
    u = a  # 控制向量,此例子中为加速度
    P = np.eye(n)  # 状态协方差矩阵初始化
    Q = np.array([[1, 0], [0, 1]])  # 外界噪音协方差矩阵
    H = np.eye(n)  # 单位转化矩阵
    R = np.array([[sd_p**2, 0], [0, sd_v**2]])  # 观测值协方差矩阵
    kf = KalmanFilter(F, B, u, Q, H, R, P)
    
    # 绘制真实值、观测值、滤波输出值三者之间的动态关系
    fusion = np.zeros([2, T-1])
    # 初始化
    x = z[:, 0].reshape((2, 1))
    # 绘图的x、y轴
    ax = []
    ay_real = []
    ay_sensor = []
    ay_fusion = []
    # 将 figure 设置为交互模式,figure 不用 plt.show() 也可以显示
    plt.ion()
    for i in range(T-1):
        x = kf.predict(x)
        x = kf.update(x, z[:, i+1].reshape((2, 1))) 
        fusion[0, i] = x[0, 0]
        fusion[1, i] = x[1, 0]
        ax.append(i+1)
        ay_real.append(p[i+1])
        ay_sensor.append(p[i+1]+noise_p[i+1])
        ay_fusion.append(x[0, 0])
        plt.clf()
        plt.plot(ax, ay_real, color='k', marker='o', markersize=5, label='real position')
        plt.plot(ax, ay_sensor, color='b', marker='^', markersize=5, label='sensory position')
        plt.plot(ax, ay_fusion, color='r', marker='s', markersize=5, label='fusion position')
        plt.legend()
        plt.pause(0.5)  # 暂停一段时间,不然画得太快会卡住显示不出来
        plt.ioff()  # 将 figure 设置为阻塞模式,也是 figure 的默认模式,figure 必须用 plt.show() 才能显示
    plt.show()
        

输出结果

在这里插入图片描述
在这里插入图片描述
直观上来说卡尔曼滤波跟踪的轨迹比检测轨迹更接近真实轨迹。并且由于观测误差的存在,检测轨迹在真实轨迹周围波动明显,而滤波轨迹明显比检测轨迹更加平滑。

如有错误,欢迎指正!

  • 2
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
基于卡尔曼滤波的目标跟踪Python的实现需要以下步骤: 1. 安装必要的Python库,如numpy和matplotlib。 2. 定义卡尔曼滤波器的状态转移矩阵A、观测矩阵H、过程噪声协方差矩阵Q、观测噪声协方差矩阵R和初始状态向量x0。 3. 读取视频或摄像头的帧并将其转换为灰度图像。 4. 对于第一帧,使用Haar级联分类器检测目标并初始化卡尔曼滤波器的状态向量x和协方差矩阵P。 5. 对于后续帧,使用卡尔曼滤波器进行目标跟踪。首先,使用预测步骤预测目标的位置。然后,使用测量步骤根据检测到的目标位置更新卡尔曼滤波器的状态向量和协方差矩阵。 6. 在每个帧上绘制目标的位置和卡尔曼滤波器的预测位置。 以下是一个基于卡尔曼滤波的目标跟踪Python的示例代码: ```python import cv2 import numpy as np # 定义卡尔曼滤波器的参数 dt = 1.0/30.0 A = np.array([[1, dt, 0, 0], [0, 1, 0, 0], [0, 0, 1, dt], [0, 0, 0, 1]]) H = np.array([[1, 0, 0, 0], [0, 0, 1, 0]]) Q = np.array([[0.01, 0, 0.0, 0], [0, 0.01, 0, 0], [0, 0, 0.01, 0], [0, 0, 0, 0.01]]) R = np.array([[0.1, 0], [0, 0.1]]) x = np.array([[0], [0], [0], [0]]) P = np.diag((0.01, 0.01, 0.01, 0.01)) # 初始化视频捕获对象 cap = cv2.VideoCapture(0) while True: # 读取帧并将其转换为灰度图像 ret, frame = cap.read() gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 如果是第一帧,则使用Haar级联分类器检测目标并初始化卡尔曼滤波器的状态向量和协方差矩阵 if ret: if cv2.waitKey(1) & 0xFF == ord('q'): break if np.sum(x) == 0: face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml') faces = face_cascade.detectMultiScale(gray, 1.3, 5) if len(faces) > 0: x[0] = faces[0][0] x[2] = faces[0][1] cv2.rectangle(frame, (int(x[0]), int(x[2])), (int(x[0])+int(x[1]), int(x[2])+int(x[3])), (0, 255, 0), 2) # 使用卡尔曼滤波器进行目标跟踪 xp = np.dot(A, x) Pp = np.dot(np.dot(A, P), A.T) + Q if len(faces) > 0: z = np.array([[faces[0][0]+faces[0][2]/2], [faces[0][1]+faces[0][3]/2]]) y = z - np.dot(H, xp) S = np.dot(np.dot(H, Pp), H.T) + R K = np.dot(np.dot(Pp, H.T), np.linalg.inv(S)) x = xp + np.dot(K, y) P = np.dot((np.eye(4) - np.dot(K, H)), Pp) # 在每个帧上绘制目标的位置和卡尔曼滤波器的预测位置 cv2.rectangle(frame, (int(x[0]), int(x[2])), (int(x[0])+int(x[1]), int(x[2])+int(x[3])), (0, 0, 255), 2) cv2.imshow('frame', frame) # 释放视频捕获对象并关闭所有窗口 cap.release() cv2.destroyAllWindows() ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值