如何使用卡尔曼滤波(Kalman Filtering)实现对物体运动轨迹的预测?(附源码)

OpenCV杂谈_13


一. 什么是卡尔曼滤波?它的用途又是什么?以及它有什么拓展?
  1. 卡尔曼滤波(Kalman filtering, KF)是一种利用线性系统状态方程,通过系统输入输出观测数据,对系统状态进行最优估计的算法。
  2. 卡尔曼滤波的一个典型实例是从一组有限的,对物体位置的,包含噪声的观察序列中预测出物体的坐标位置及速度。在很多工程应用(雷达、计算机视觉)中都可以找到它的身影。同时,卡尔曼滤波也是控制理论以及控制系统工程中的一个重要话题。
  3. 为了解决普通KF仅能处理线性数据的问题,从而就有了用于解决非线性数据的基于KF的推展:拓展卡尔曼滤波(EKF)和无迹卡尔曼滤波(UKF)。KF主要应用于系统方程和量测方程为线性的的场景,EKF应用于轻度非线性场景,而UKF则在强非线性情况下有更好的表现。
二. 环境配置
  1. Python版本:3.7
  2. 功能包:opencv-python(4.5.3.56)、 numpy (1.21.1)
  3. 文件夹配置:
    bydemo
三. 使用简单的线性数据做预测实验

讲解:通过以下两个.py文件,简单实现了对随机点位置的预测,结果展示中红色的点代表着手动录入的点,白色的圈则是根据已给出的红色点进行的卡尔曼滤波预测,黑色的圈则是根据白色的圈进行的卡尔曼滤波预测。

  1. klamanfilter.py
import cv2
import numpy as np


class KalmanFilter:
    kf = cv2.KalmanFilter(4, 2)
    kf.measurementMatrix = np.array([[1, 0, 0, 0], [0, 1, 0, 0]], np.float32)
    kf.transitionMatrix = np.array([[1, 0, 1, 0], [0, 1, 0, 1], [0, 0, 1, 0], [0, 0, 0, 1]], np.float32)

    def predict(self, coordX, coordY):
        '''This function estimates the position of the object'''
        measured = np.array([[np.float32(coordX)], [np.float32(coordY)]])
        self.kf.correct(measured)
        predicted = self.kf.predict()
        x, y = int(predicted[0]), int(predicted[1])
        return x, y
  1. main.py
"""用一个实例代码讲述一下什么是卡尔曼滤波器其本质其实就是用来预测之后物体的运动位置"""

from kalmanfilter import KalmanFilter
import cv2

# 初始化 Kalman 滤波器
kf = KalmanFilter()

img = cv2.imread("background.png")
img = cv2.resize(img, (1280, 720))

ball_positions = [(4, 300), (61, 256), (116, 214), (170, 180), (225, 148), (279, 120), (332, 97),
         (383, 80), (434, 66), (484, 55), (535, 49), (586, 49), (634, 50),
         (683, 58), (731, 69), (778, 82), (824, 101), (870, 124), (917, 148),
         (962, 169), (1006, 212), (1051, 249), (1093, 290)]

for pt in ball_positions:  # 先用之前规定好的点预测出第一个预测点
    cv2.circle(img, pt, 5, (0, 0, 255), -1)

    predicted = kf.predict(pt[0], pt[1])
    cv2.circle(img, predicted, 5, (255, 255, 255), 4)

for i in range(10):  # 用预测出的第一个预测点作为新预测点的评估对象,得出第二个预测点,之后再将第二个预测点作为第三个预测点的评估对象,得到第三个预测点,反反复复得到十个预测点
    predicted = kf.predict(predicted[0], predicted[1])
    cv2.circle(img, predicted, 5, (0, 0, 0), 4)

cv2.imshow("Img", img)
cv2.waitKey(0)

结果展示:
bydemo

四. 卡尔曼滤波器在真实世界中的使用
  1. 对抛出橘子的运动轨迹预测:图中蓝色框表示的是对橘子的识别后生成的bounding box,蓝色的点表示的是bounding box的中心点,黑色的圈表示的是对橘子在图像下一帧中的运动轨迹的预测。
    bydemo
  2. 对路上行人的运动轨迹预测:图中蓝色框表示的是对人体的识别后生成的bounding box,蓝色的点表示的是bounding box的中心点,黑色的圈表示的是对人体在图像下一帧中的运动轨迹的预测。

bydemo

五. 感悟与分享
  1. 在文章的第四部分分别对符合自由落体规律的橘子以及毫无规律可言的人的运动轨迹进行了基于普通卡尔曼滤波的轨迹预测,可见在预测精确度上有着明显的差异,换句话说就是,普通卡尔曼滤波在对有规律可言的线性数据进行预测时有着不错的表现,但对于毫无规律可言的非线性数据进行预测时,其精度表现差强人意。
  2. 分享:
    1. 百度百科 - - 卡尔曼滤波
    2. 浅谈卡尔曼滤波
    3. 详解卡尔曼滤波
  3. 代码参考:Predict trajectory of an Object with Kalman filter(内容为英文)

如有问题,敬请指正。欢迎转载,但请注明出处。
### 使用C++实现卡尔曼滤波进行实时预测 #### 创建 KalmanFilter 类 为了便于管理和重用,可以创建一个 `KalmanFilter` 类来封装卡尔曼滤波的核心逻辑。该类负责初始化参数、执行预测和更新步骤。 ```cpp #include <iostream> #include <Eigen/Dense> using Eigen::MatrixXd; using Eigen::VectorXd; class KalmanFilter { public: void Init(VectorXd &x_in, MatrixXd &P_in, MatrixXd &F_in, MatrixXd &H_in, MatrixXd &R_in, MatrixXd &Q_in); void Predict(); void Update(const VectorXd &z); private: VectorXd x_; // 状态向量 MatrixXd P_; // 估计误差协方差矩阵 MatrixXd F_; // 状态转移矩阵 MatrixXd H_; // 测量矩阵 MatrixXd R_; // 测量噪声协方差矩阵 MatrixXd Q_; // 过程噪声协方差矩阵 }; void KalmanFilter::Init(VectorXd &x_in, MatrixXd &P_in, MatrixXd &F_in, MatrixXd &H_in, MatrixXd &R_in, MatrixXd &Q_in) { x_ = x_in; P_ = P_in; F_ = F_in; H_ = H_in; R_ = R_in; Q_ = Q_in; } void KalmanFilter::Predict() { x_ = F_ * x_; MatrixXd Ft = F_.transpose(); P_ = F_ * P_ * Ft + Q_; } void KalmanFilter::Update(const VectorXd &z) { VectorXd z_pred = H_ * x_; VectorXd y = z - z_pred; MatrixXd Ht = H_.transpose(); MatrixXd S = H_ * P_ * Ht + R_; MatrixXd Si = S.inverse(); MatrixXd PHt = P_ * Ht; MatrixXd K = PHt * Si; // 更新状态和协方差 x_ = x_ + (K * y); long x_size = x_.size(); MatrixXd I = MatrixXd::Identity(x_size, x_size); P_ = (I - K * H_) * P_; } ``` 此代码片段定义了一个基本的卡尔曼滤波器框架[^1]。 #### 初始化并运行过滤器 下面是如何设置初始条件以及调用上述方法来进行一次完整的迭代: ```cpp int main(){ // 定义变量维度 int n = 2; // 假设我们只关心位置和速度 // 初始猜测的状态向量 [position velocity]' VectorXd x(2); x << 0., 0.; // 初始估计误差协方差矩阵 MatrixXd P(2, 2); P << 1000., 0., 0., 1000.; // 预测下一个时刻的状态转换矩阵 float dt = 1.; // 时间间隔为一秒 MatrixXd F(2, 2); F << 1., dt, 0., 1.; // 测量函数(假设可以直接观测到位置) MatrixXd H(1, 2); H << 1., 0.; // 测量不确定性 MatrixXd R(1, 1); R << 1.; // 过程不确定性 MatrixXd Q(2, 2); Q << .1, 0., 0., .1; // 构造对象并传入参数 KalmanFilter kf; kf.Init(x, P, F, H, R, Q); while(true){ // 获取传感器读数... double measurement = /* ... */; // 执行预测步 kf.Predict(); // 将新数据加入系统中 VectorXd measurment_vector(1); measurment_vector << measurement; kf.Update(measurment_vector); std::cout << "Updated state: \n" << kf.x_ << "\n"; sleep(dt); // 模拟等待下一周期到来 } return 0; } ``` 这段程序展示了如何利用之前定义好的 `KalmanFilter` 来完成从初始化到持续接收输入直到输出最终结果的过程。
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值