opencv实现卡尔曼滤波

news2024/10/2 16:27:56

卡尔曼滤波是一种递归的估计,即只要获知上一时刻状态的估计值以及当前状态的观测值就可以计算出当前状态的估计值,因此不需要记录观测或者估计的历史信息。

卡尔曼滤波器分为两个阶段:预测更新。在预测阶段,滤波器使用上一状态的估计,做出对当前状态的估计。在更新阶段,滤波器利用对当前状态的观测值优化在预测阶段获得的预测值,以获得一个更精确的新估计值。

opencv中有KalmanFilter类,参考【1】

class CV_EXPORTS_W KalmanFilter
{
public:
    CV_WRAP KalmanFilter();

    //dynamParams状态的维度;measureParams 测量值的维度;controlParams控制向量的维数。
    CV_WRAP KalmanFilter( int dynamParams, int measureParams, int controlParams = 0, int type = CV_32F );

    //重新初始化卡尔曼滤波器
    void init( int dynamParams, int measureParams, int controlParams = 0, int type = CV_32F );

    //计算预测的状态,返回 statePre
    CV_WRAP const Mat& predict( const Mat& control = Mat() );

    //根据测量(观测)结果更新预测状态,返回statePost
    //measurement不能通过观测方程进行计算得到,要自己定义
    CV_WRAP const Mat& correct( const Mat& measurement );

    /*
       x(k) = A*x(k-1) + B*u(k)+w(k)         运动方程(w(k)协方差为Q)
       z(k) = H*x(k)   + v(k)                观测方程 (v(k)协方差为R)
    */
    CV_PROP_RW Mat statePre;           //预测值  (x'(k)): x(k)=A*x(k-1)+B*u(k)
    CV_PROP_RW Mat statePost;          //状态值  (x(k)): x(k)=x'(k)+K(k)*(z(k)-H*x'(k))
    CV_PROP_RW Mat transitionMatrix;   //状态转移矩阵 (A)
    CV_PROP_RW Mat controlMatrix;      //控制矩阵 B  
    CV_PROP_RW Mat measurementMatrix;  //测量矩阵 H
    CV_PROP_RW Mat processNoiseCov;    //系统误差 Q
    CV_PROP_RW Mat measurementNoiseCov;//测量误差 R
    CV_PROP_RW Mat errorCovPre;        //先验协方差(P'(k)): P'(k)=A*P(k-1)*A^T + Q)
    CV_PROP_RW Mat gain;               //卡尔曼增益 K(k)=P'(k)*H^T*inv(H*P'(k)*H^T+R)
    CV_PROP_RW Mat errorCovPost;       //后验协方差 (P(k)): P(k)=(I-K(k)*H)*P'(k)

    // temporary matrices
    Mat temp1;
    Mat temp2;
    Mat temp3;
    Mat temp4;
    Mat temp5;
};

x(k) = A*x(k-1) + B*u(k)+w(k)   运动方程
z(k) = H*x(k)   + v(k)                观测方程

公式用到的变量:参考【2】

(1)定义三个列向量:

  • 状态向量x(k):state
  • 观测向量z(k):measurement
  • 控制向量:u(k):control

  (2)三个矩阵

  • 状态转移矩阵A:transitionMatrix
  • 控制矩阵B:controlMatrix
  • 测量矩阵H:measurementMatrix

   (3)估计的状态向量和协方差

k-1时刻

  • 后验估计的状态向量\hat{X}_{k-1}(statePost)  : k-1时刻的最优的状态估计
  • 后验估计的状态向量的协方差矩阵\hat{P}_{k-1}(errorCovPost):k-1时刻的最优的状态估计的协方差

k时刻:

  • 先验估计的状态向量\bar{X}_{k}(statePre):k时刻的预测的状态估计
  • 先验估计的状态向量的协方差矩阵\bar{P}_{k}(errorCovPre):k时刻的预测的状态估计的协方差
  • 后验估计的状态向量\hat{X}_{k}(statePost):求出k时刻的最优的状态估计
  • 后验估计的状态向量的协方差矩阵\hat{P}_{k}(errorCovPost):求出k时刻的最优的状态估计的协方差

 卡尔曼滤波做的是:用k-1时刻的后验数据求k时刻的先验;k时刻的先验数据求k时刻的后验数据(最优的状态估计)

(4)噪声及协方差矩阵

  • 系统噪声w(k):协方差为Q (processNoiseCov)
  • 测量噪声v(k):协方差R(measurementNoiseCov)

例1 opencv自带程序:建立一个绕某一圆心做匀速圆周运动的小球,但是实际中它会受到系统噪声影响从而其角度和角速度有所变化,通过带有噪声的观测值(真实值+观测噪声)和匀速运动模型的预测值为输入使用KF得到估计值。

在一个圆弧上运动,只有一个自由度:角度。是一个匀速运动,建立匀速运动模型,设定状态变量x = [ x1, x2 ] = [ 角度,△角度]

具体代码如下:

#include "opencv2/video/tracking.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <stdio.h>
using namespace std;
using namespace cv;
 
//根据圆心和夹角计算点的二维坐标
static inline Point calcPoint(Point2f center, double R, double angle)
{
    return center + Point2f((float)cos(angle), (float)-sin(angle))*(float)R;  // 图像坐标系中y轴向下
}
//为什么cos(angle)没有乘R??? 


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"
            );
}

int main(int, char**)
{
    //help();
    Mat img(500, 500, CV_8UC3);
    
    /** @brief 创建卡尔曼滤波器对象KF.
    @param dynamParams 2,状态向量的维度,二维列向量(角度,△角度)
    @param measureParams 1,测量向量的维度,一维列向量(角度)
    @param controlParams 0,控制向量的维度  
     */ 
    KalmanFilter KF(2, 1, 0);            
    Mat state(2, 1, CV_32F);                                                              //状态向量x(k)<=>state              (角度,△角度)  
    Mat measurement = Mat::zeros(1, 1, CV_32F);                 // 测量向量z(k)

    Mat processNoise(2, 1, CV_32F);                                            //系统状态噪声w(k) 
   
    char code = (char)-1;
     
    for(;;)
    {
		
        //返回高斯分布的随机矩阵
        randn( state, Scalar::all(0), Scalar::all(0.1) );  //初始化系统状态真实值 

       /*初始化三个矩阵:状态转移矩阵Ak(transitionMatrix),控制矩阵Bk(controlMatrix)和测量矩阵Hk(measurementMatrix)*/
        KF.transitionMatrix = (Mat_<float>(2, 2) << 1, 1, 0, 1);  // 匀速运动模型中的状态转移矩阵A
        setIdentity(KF.measurementMatrix);                             //测量矩阵H [1,0]
        // 控制向量都没有,控制矩阵Bk(controlMatrix)更没有

        /*两个噪声的协方差矩阵*/
        setIdentity(KF.processNoiseCov, Scalar::all(1e-5));            //系统噪声方差矩阵Q[9.9999997e-06, 0;  0, 9.9999997e-06]
        setIdentity(KF.measurementNoiseCov, Scalar::all(1e-1));        //测量噪声方差矩阵R   [0.1] 

        /*应该是指k-1时刻的后验状态和协方差*/
        randn(KF.statePost, Scalar::all(0), Scalar::all(0.1));          //初始化后验估计的状态向量
        setIdentity(KF.errorCovPost, Scalar::all(1));                  //初始化后验协方差P(k)  [1, 0;0, 1

        for(;;)
        {
            Point2f center(img.cols*0.5f, img.rows*0.5f);         
            float R = img.cols/3.f; 

            /*真实点*/                                                
            double stateAngle = state.at<float>(0);                //跟踪点角度
            Point statePt = calcPoint(center, R, stateAngle);     //真实位置

            /*预测点*/
            Mat prediction = KF.predict();                       //计算预测值
            double predictAngle = prediction.at<float>(0);       
            Point predictPt = calcPoint(center, R, predictAngle);   //预测位置
           
           /*测量点(观测点)*/
            randn( measurement, Scalar::all(0), Scalar::all(KF.measurementNoiseCov.at<float>(0)));     //给measurement赋N(0,R)的随机值
            measurement += KF.measurementMatrix*state;  //观测位置 = 真实位置 + 观测位置噪声   (z = H*x(k) + R)
            double measAngle = measurement.at<float>(0);
            Point measPt = calcPoint(center, R, measAngle);  //观测位置
 
          /*估计点*/
            if(theRNG().uniform(0,4) != 0)                      //返回[0,4)范围内均匀分布的整数随机数,即四分之一机会返回0   
                         KF.correct(measurement);
           Mat  gujizhi = KF.correct(measurement);   // 使用观测值更新估计值,函数返回值是最优状态估计(statePost)
           double gujiAngle = gujizhi.at<float>(0);  
           Point gujiPt = calcPoint(center, R, gujiAngle);  //估计位置

            #define drawCross( center, color, d )                                 \
                line( img, Point( center.x - d, center.y - d ),                \
                             Point( center.x + d, center.y + d ), color, 1, CV_AA, 0); \
                line( img, Point( center.x + d, center.y - d ),                \
                             Point( center.x - d, center.y + d ), color, 1, CV_AA, 0 )
 
            img = Scalar::all(0);
            circle(img, center, R, Scalar::all(70), 1);
             /// 实时更新三个位置
            drawCross( statePt, Scalar(255,255,255), 3 );
            drawCross( measPt, Scalar(0,0,255), 3 );
            drawCross( predictPt, Scalar(0,255,0), 3 );
            drawCross( gujiPt, Scalar(255,0,0), 3 );

            //line( img, statePt, measPt, Scalar(0,0,255), 3, CV_AA, 0 );  //真实和观测(红色)
            //line( img, statePt, predictPt, Scalar(255,0,0), 3, CV_AA, 0 );  //真实和预测(蓝色)
            //line( img, statePt, gujiPt, Scalar(255,0,0), 3, CV_AA, 0 );  //真实和估计(蓝色)
           
            randn( processNoise, Scalar(0), Scalar::all(sqrt(KF.processNoiseCov.at<float>(0, 0))));  
            state = KF.transitionMatrix*state + processNoise;   //加入噪声的state
 
            imshow( "Kalman", img );
            code = (char)waitKey(100);
 
            if( code > 0 )
                break;
        }
        if( code == 27 || code == 'q' || code == 'Q' )    //ASCII中27是ESC
            break;
    }
    return 0; 
}

原有的代码中比较的是真实值与预测值、真实值与观测值的偏差,没有用到估计值,因此我在上述代码中加入了真实值与估计值的比较。也可以把画线函数注释了,只看四个点的移动 。

真实值:

预测值:KF.predict()

观测值:真实值+观测噪声

估计值:KF.correct(measurement);   // 使用观测值更新估计值

例2 跟踪鼠标位置

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

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

相关文章

VScode配置远端服务器深度学习项目

前提准备已安装VScode。 1.安装插件Remote Development 安装完成后左侧就多了远程资源管理器图标&#xff1a; 1.点击远程资源管理器。 2.点击小齿轮&#xff08;配置&#xff09;。 3.选择config配置文件&#xff0c;如果没有自己按照相似路径新建config文件后重复1、2、3步骤…

组合总和III

组合总和III 题目 力扣题目链接:https://leetcode.cn/problems/combination-sum-iii/ 代码 class Solution {public:vector<vector<int

小航助学答题系统编程等级考试scratch一级真题2023年3月(含题库答题软件账号)

青少年编程等级考试scratch真题答题考试系统请点击 电子学会-全国青少年编程等级考试真题Scratch一级&#xff08;2019年3月&#xff09;在线答题_程序猿下山的博客-CSDN博客_小航答题助手 1.下列说法不正确的是&#xff1f;&#xff08; &#xff09; A.可以从声音库中随机…

【JSP学习笔记】7.JSP 过滤器

JSP 过滤器 JSP 和 Servlet 中的过滤器都是 Java 类。 过滤器可以动态地拦截请求和响应&#xff0c;以变换或使用包含在请求或响应中的信息。 可以将一个或多个过滤器附加到一个 Servlet 或一组 Servlet。过滤器也可以附加到 JavaServer Pages (JSP) 文件和 HTML 页面。 过…

20230419 | 704.二分查找、27.移除元素

1、数组基础理论 int a[m][n]; 数组长度表示&#xff1a;a[0].length 数组宽度表示&#xff1a;a.length 2、704.二分查找 特征&#xff1a;数组是升序的找某个数&#xff0c;那就使用二分法。时间复杂度O(log n)&#xff0c;空间复杂度O(1) 我使用左闭右闭区间 计算中点&…

22、原理解析

文章目录 1、Profile功能1、application-profile功能2、Profile条件装配功能3、profile分组 2、外部化配置1、外部配置源2、配置文件查找位置3、配置文件加载顺序&#xff1a;4、指定环境优先&#xff0c;外部优先&#xff0c;后面的可以覆盖前面的同名配置项 3、自定义starter…

P600旗舰视觉款正式发布,重新定义视觉追踪与精准定位!

P600旗舰视觉款无人机是一款准行业级无人机&#xff0c;搭载RTK定位系统&#xff0c;定位精度可达厘米级&#xff0c;飞行路径更精准、姿态更稳定&#xff1b;机身搭载Allspark机载计算机&#xff0c;算力可达21TOPS&#xff0c;可运行大部分主流算法&#xff1b;配置G1三轴吊舱…

共模电感是如何抑制共模信号的

这是一个共模电感&#xff0c;外观它和我们常用的电感最大的区别就是共模电感有四个引脚&#xff0c;共模电感的磁芯上绕着两组线圈&#xff0c;这两个线圈匝数和材料都是一样的。 共模电感最主要的作用就是能抑制共模信号&#xff0c;一般用在电源或信号的EMI电路中。 首先来…

【ROS实操3服务调用添加乌龟数量】

需求描述 编码实现向turtlesim 发送请求&#xff0c;在乌龟显示节点的窗体指定位置生成一乌龟&#xff0c;这是一个服务请求操作。 实现分析 1.首先&#xff0c;需要启动乌龟显示节点。 2.要通过ROS命令&#xff0c;来获取乌龟生成服务的服务名称以及服务消息类型。 3.编写服…

C++基础入门——语法详解篇(上)

文章目录 一、什么是 C 呢&#xff1f; 二、为什么要学 C 呢&#xff1f; 三、C 基础语法 3、1 C 关键字 3、2 命名空间 3、2、1 为什么要引入命名空间 3、2、2 命名空间的定义 3、2、3 命名空间的使用 3、3 C的输入和输出 3、4 函数重载 3、4、1 函数重载的概念 3、4、2 C支持…

【WAF】雷池安装及使用体验

文章目录 前言一、雷池介绍二、安装及使用1.下载链接2.下载3. 安装waf测试 前言 长亭一直是我比较喜欢的一家公司&#xff0c;像业界比较出名的扫描器xray还有rad等很多工具都是他们开发的&#xff0c;使用起来非常的nice&#xff0c;联动其他安全工具可以实现自动漏洞挖掘&am…

掌玩科技×OceanBase:HTAP实时数据分析,降低80%存储成本

欢迎访问 OceanBase 官网获取更多信息&#xff1a;https://www.oceanbase.com/ 近日&#xff0c;新兴游戏公司海南掌玩网络科技有限公司&#xff08;以下简称“掌玩科技”&#xff09;正式牵手原生分布式数据库 OceanBase&#xff0c;其投放系统、用户分析系统、数据系统、运营…

beef-xss浏览器劫持

beef-xss浏览器劫持 一&#xff0c;实验拓扑图二&#xff0c;租用一台阿里云&#xff0c;搭建docker环境和beef环境1.租一台阿里云服务器&#xff0c;系统选用ubuntu&#xff0c;计时收费的那种&#xff0c;一个小时几毛钱2.开启策略组3000端口&#xff0c;5000端口4.安装docke…

wait/notify使用详解

1. 使用注意事项 wait/notify(All)可用于线程间(线程数量>3)通信 永远在synchronized方法或对象里使用wait/notify(All),不然JVM报java.lang.IllegalMonitorStateException 永远在while循环里使用wait&#xff0c;防止其他原因改变先前判断条件 永远在线程间共享对象(生产…

直流有刷电机的电路分析

这里写目录标题 H桥改进后的电路L298N原理图野火的电机驱动板MOS管野火的原理图 H桥 当 Q1 和 Q4 导通时&#xff0c;电流将经过 Q1 从左往右流过电机&#xff0c;在经过 Q4 流到电源负极&#xff0c;这时图中电机可以顺时针转动。 当 Q3 和 Q2 导通时&#xff0c;电流将经过 Q…

【AI算法学习】基于AutoEncoder的生成对抗网络

基于AutoEncoder的生成对抗网络&#xff1a;VAE-GAN AutoEncoderVAEGANVAE-GANDCGANInfoGANss-InfoGAN论文链接 " 生成模型&#xff08;Generative modeling&#xff09;"已成为机器学习的一个较为广泛的领域。在图像这种流行数据上&#xff0c;每张图像都有数千数万…

服务(第七篇)nginx优化

隐藏版本号&#xff1a; 方法①&#xff1a; 修改配置文件&#xff1a; 检测&#xff1a;版本号没有了 方法②&#xff1a; 首先修改nginx.h文件&#xff0c;修改版本号和服务名&#xff1a; 然后切换到/opt/nginx-1.18.0进行编译安装&#xff1a; 安装后进入nginx.conf进行…

Redis缓存实战(2)

目录 缓存定义 Redis缓存实战 1删除缓存还是更新缓存&#xff1f; 2如何保证缓存与数据库的操作同时成功或者失败&#xff1f; 3先操作数据库还是缓存&#xff1f; 缓存问题 缓存穿透 缓存雪崩 缓存击穿 缓存定义 缓存&#xff08;Cache)是数据交换的缓冲区&#xff0…

微信小程序自动化测试实战教程,框架源码应有尽有

目录 1. 微信小程序自动化测试介绍 2. 搭建微信小程序自动化测试框架 步骤1&#xff1a;选择测试工具 步骤2&#xff1a;搭建测试环境 步骤3&#xff1a;编写测试脚本 步骤4&#xff1a;执行测试 3. 实现微信小程序自动化测试的关键技术 技术1&#xff1a;微信小程序自动…

Netty:常见的面试题和答案

1. 什么是Netty&#xff1f; 答&#xff1a;Netty是一个高性能的网络编程框架&#xff0c;基于NIO的非阻塞式IO模型&#xff0c;可以帮助开发者快速开发高性能、高可靠性的网络应用程序。 2. Netty的核心组件有哪些&#xff1f; 答&#xff1a;Netty的核心组件包括&#xff…