一文理解粒子滤波

0. 粒子滤波流程

之前学习记录的文档,这里也拿出来分享一下~

基本原理:随机选取预测域的 N NN 个点,称为粒子。以此计算出预测值,并算出在测量域的概率,即权重,加权平均就是最优估计。之后按权重比例,重采样,进行下次迭代。

  1. 初始状态:用大量粒子模拟X(t),粒子在空间内均匀分布;

  2. 预测阶段:根据状态转移方程,每一个粒子得到一个预测粒子;

  3. 校正阶段:对预测粒子进行评价,越接近于真实状态的粒子,其权重越大;

  4. 重采样:根据粒子权重对粒子进行筛选,筛选过程中,既要大量保留权重大的粒子,又要有一小部分权重小的粒子;

  5. 滤波:将重采样后的粒子带入状态转移方程得到新的预测粒子,即步骤2。

    在这里插入图片描述

1. 初始状态

在这里插入图片描述

我们假设 GPS 的位置及航向输出服从正态分布,因此在得到 GPS 的初始输出后,我们可以根据初始值(均值 μ)和 GPS 观测不确定度(标准差 σ)构造无人车的定位初始分布,并通过对初始分布进行随机采样完成粒子集的初始化。

/**
 * @brief Initialize the particle filter.
 *
 * @param x Initial x position [m] from GPS.
 * @param y Initial y position [m] from GPS.
 * @param theta Initial heading angle [rad] from GPS.
 * @param std_pos Array of dimension 3 [standard deviation of x [m],
 *   standard deviation of y [m], standard deviation of theta [rad]]
 */
void ParticleFilter::Init(const double &x, const double &y, const double &theta,
                          const double std_pos[])
{
    if (!IsInited())
    {
        // create normal distributions around the initial gps measurement values
        std::default_random_engine gen;
        std::normal_distribution<double> norm_dist_x(x, std_pos[0]);
        std::normal_distribution<double> norm_dist_y(y, std_pos[1]);
        std::normal_distribution<double> norm_dist_theta(theta, std_pos[2]);

        // initialize particles one by one
        for (size_t i = 0; i < n_p; ++i)
        {
            particles(0, i) = norm_dist_x(gen);
            particles(1, i) = norm_dist_y(gen);
            particles(2, i) = norm_dist_theta(gen);
        }

        // initialize weights to 1 / n_p
        weights_nonnormalized.fill(1 / n_p);
        weights_normalized.fill(1 / n_p);

        is_inited = true;
    }
}

有几点说明如下:

  • 这里使用了 C++ 的随机数引擎 std::default_random_engine 和正态分布模板类 std::normal_distribution 实现了高斯随机数的生成
  • particles 3 × n p 3\times n_p 3×np 的粒子 Eigen 矩阵, n p n_p np 表示粒子数目,我们在构造函数的初始值列表中将其初始化为了 1000 1000 1000,矩阵的每一列表示一个粒子的状态,矩阵的第 0 0 0 1 1 1 2 2 2 行分别表示粒子的横向位置 x x x、纵向位置 y y y 和航向角 t h e t a \\theta theta
  • weights_nonnormalizedweights_normalized 分别表示未归一化和归一化的重要性权重,数据类型都是 n p × 1 n_p\times 1 np×1 的 Eigen 向量。很多粒子滤波教程中使用同一个变量存放未归一化和归一化的重要性权重,这样也是可以的,这里我们的目的是使代码逻辑更加清晰。

2. 预测阶段

根据机器人的车轮运动速度或者里程对粒子进行状态转移,即将粒子的信息带入机器人的运动模型中,加入控制噪声并产生新的粒子。

在预测步中,我们需要根据无人车的运动模型、车速、航向角速率、相邻两帧的时间间隔等将上一步的粒子集向当前时刻进行预测。这里我们我们假设自车遵从 CRTV 运动模型,关于 CRTV,在此前文章《从贝叶斯滤波到无迹卡尔曼滤波》中我们已经介绍过,不再赘述,这里我们这里直接给出不计噪声时的 CRTV 状态方程

在这里插入图片描述

式 (5.1) 中, ω \omega ω 即自车的航向角速率。CTRV 是 CV 的一般形式,当 ω = 0 \omega = 0 ω=0 时,CTRV 退化为 CV。

/**
 * @brief Predict new state of particle according to the system motion model.
 *
 * @param velocity Velocity of car [m/s]
 * @param yaw_rate Yaw rate of car [rad/s]
 * @param delta_t delta time between last timestamp and current timestamp [s]
 * @param std_pos Array of dimension 3 [standard deviation of x [m],
 *   standard deviation of y [m], standard deviation of yaw [rad]]
 */
void ParticleFilter::Predict(const double &velocity, const double &yaw_rate,
                             const double &delta_t, const double std_pos[])
{
    if (!IsInited())
        return;

    // create process noise's normal distributions of which the mean is zero
    std::default_random_engine gen;
    std::normal_distribution<double> norm_dist_x(0, std_pos[0]);
    std::normal_distribution<double> norm_dist_y(0, std_pos[1]);
    std::normal_distribution<double> norm_dist_theta(0, std_pos[2]);

    // predict state of particles one by one
    for (size_t i = 0; i < n_p; ++i)
    {
        double theta_last = particles(2, i);

        Eigen::Vector3d state_trans_item_motion;
        Eigen::Vector3d state_trans_item_noise;

        state_trans_item_noise << norm_dist_x(gen), norm_dist_y(gen), norm_dist_theta(gen);

        if (std::fabs(yaw_rate) > 0.001) // CTRV model
        {
            state_trans_item_motion << velocity / yaw_rate * (sin(theta_last + yaw_rate * delta_t) - sin(theta_last)),
                velocity / yaw_rate * (-cos(theta_last + yaw_rate * delta_t) + cos(theta_last)),
                yaw_rate * delta_t;
        }
        else // approximate CV model
        {
            state_trans_item_motion << velocity * cos(theta_last) * delta_t,
                velocity * sin(theta_last) * delta_t,
                yaw_rate * delta_t;
        }

        // predict new state of the ith particle
        particles.col(i) = particles.col(i) + state_trans_item_motion + state_trans_item_noise;

        // normalize theta
        NormalizeAngle(particles(2, i));
    }
}

状态转移过程中的过程噪声我们假设为零均值的高斯白噪声。很明显预测步只改变了每个粒子的状态,未改变粒子的权重。每个粒子的预测航向角我们都做了 [ − π , π ] [-\pi, \pi] [π,π] 的归一化处理,后面在计算系统最终的加权状态估计时不需要重复处理。

粒子滤波基于贝叶斯滤波框架。在贝叶斯滤波中,我们试图估计系统的状态(状态变量) x t x_t xt,其中 t t t表示时间步。贝叶斯滤波的核心思想是使用贝叶斯定理来更新状态的后验概率分布,即 P ( x t ∣ z 1 : t , u 1 : t ) P(x_t | z_{1:t}, u_{1:t}) P(xtz1:t,u1:t),其中 z 1 : t z_{1:t} z1:t表示观测序列, u 1 : t u_{1:t} u1:t表示控制输入序列。

在这里插入图片描述

在粒子滤波中 x t x_t xt是在时刻 t t t的状态, u t u_t ut是时刻 t t t的控制输入, w t w_t wt是过程噪声,表示系统模型中的不确定性

在这里插入图片描述

3. 校正阶段

这里面其实涉及到几个步骤,其实主要起到的作用是更新+粒子权重更新的部分。

更新步的目的是根据最新的路标观测结果(自车局部坐标系下的横纵向相对位置),更新预测步后每个粒子的重要性权重。更新步主要由以下四个子步骤组成,需要对粒子集中的每个粒子依次执行以下步骤,我们结合代码进行阐述。

步骤 (1): 坐标变换

无人车实时观测到的路标结果基于自车局部坐标系,我们将其转换到地图的全局坐标系,关于坐标系变换推导并不复杂,可见参考 34。假设当前时刻自车观测到某个路标 l m r k ( x c , y c ) lmrk(x_c, y_c) lmrk(xc,yc),下角标 c c c 表示自车坐标系,该路标对应于地图坐标系中的位置为 l m r k ( x m , y m ) lmrk(x_m, y_m) lmrk(xm,ym),下角标 m m m 表示地图坐标系。对于粒子 p ( x p , y p , θ p ) p(x_p, y_p, \theta_p) p(xp,yp,θp),下角标 p p p 表示粒子,我们直接给出从 l m r k ( x c , y c ) lmrk(x_c, y_c) lmrk(xc,yc) l m r k ( x m , y m ) lmrk(x_m, y_m) lmrk(xm,ym) 的坐标变换方程。

在这里插入图片描述

/**
 * @brief Transform observed landmarks from local ego vehicle coordinate to
 *   global map coordinate.
 *
 * @param lmrks_obs Observed landmarks in ego vehicle coordinate.
 * @param particle Single particle with state of [x, y, theta]
 * @param lmrks_trans2map Observed landmarks transformed from local ego vehicle
 *   coordinate to global map coordinate.
 */
void ParticleFilter::TransLandmarksFromVehicle2Map(const std::vector<LandMark_Obs> &lmrks_obs,
                                                   const Eigen::Vector3d &particle,
                                                   std::vector<LandMark_Map> &lmrks_trans2map)
{
    for (size_t i = 0; i < lmrks_obs.size(); ++i)
    {
        lmrks_trans2map[i].x = lmrks_obs[i].x * cos(particle(2)) -
                               lmrks_obs[i].y * sin(particle(2)) + particle(0);

        lmrks_trans2map[i].y = lmrks_obs[i].x * sin(particle(2)) +
                               lmrks_obs[i].y * cos(particle(2)) + particle(1);
    }
}

这一部分其实就是构建观测模型,其描述了如何将系统状态映射到观测值。通常,这可以用一个非线性函数来表示。其中, z t z_t zt是在时刻 t t t的观测值, v t v_t vt是观测噪声,表示观测模型中的不确定性。如果是深度学习给到的分割结果,这里可以直接用输入数据(如上)

在这里插入图片描述

步骤 (2): 查找传感器感知范围内的地图路标

传感器的实际感知范围是有限的,我们需要找到每个粒子对应的传感器感知范围内的地图路标。

/**
 * @brief Find map landmarks within the sensor measuremet range.
 *
 * @param lmrks_map All map landmarks.
 * @param particle Single particle with state of [x, y, theta]
 * @param snsr_range Sensor measuremet range.
 * @param lmrks_within_range Map landmarks within the sensor measuremet range.
 */
void ParticleFilter::FindMapLandmarksWithinSensorRange(const std::vector<LandMark_Map> &lmrks_map,
                                                       const Eigen::Vector3d &particle,
                                                       const double &snsr_range,
                                                       std::vector<LandMark_Map> &lmrks_within_range)
{
    static double distance_threshold_square = snsr_range * snsr_range;

    for (auto landmark : lmrks_map)
    {
        double distance_square = std::pow(particle(0) - landmark.x, 2) +
                                 std::pow(particle(1) - landmark.y, 2);

        if (distance_square <= distance_threshold_square)
            lmrks_within_range.push_back(landmark);
    }
}

步骤 (1) 和步骤 (2) 作为步骤 (3) 的输入,其顺序无关紧要。

步骤 (3): 数据关联

数据关联的目的是找到观测路标与实际地图路标的一一对应关系,步骤 (4) 中需要通过这个对应关系更新每个粒子的权重。这里我们使用一种最为简单的数据关联方法——最近邻(Nearest Neighbor,NN)数据关联,其核心思想很直观:对于两个待关联的数据集,数据间的欧氏距离越小,关联的概率越高。NN 数据关联方法的优缺点总结如下(图片出自 Udacity)。

…详情请参照古月居

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

英文翻译工具怎么选?这4款值得收藏。

英语作为国际通用语言&#xff0c;在我们的日常生活中一直有着很重要的地位&#xff0c;往大了说可以促进国际交流&#xff0c;实现文化传播&#xff1b;往小了说&#xff0c;可以解决很多生活中的小问题。但是在很多情况下英文仍旧是我们一个语言障碍&#xff0c;所以好的翻译…

网络学习-eNSP配置ACL

AR1路由器配置 <Huawei>system-view Enter system view, return user view with CtrlZ. [Huawei]undo info-center enable Info: Information center is disabled. [Huawei]interface gigabitethernet 0/0/0 [Huawei-GigabitEthernet0/0/0]ip address 192.168.2.254 24 …

MapSet之相关概念

系列文章&#xff1a; 1. 先导片--Map&Set之二叉搜索树 2. Map&Set之相关概念 目录 1.搜索 1.1 概念和场景 1.2 模型 2.Map的使用 2.1 关于Map的说明 2.2 关于Map.Entry的说明 2.3 Map的常用方法说明 3.Set的说明 3.1关于Set说明 3.2 常见方法说明 1.搜…

windows 环境下搭建mysql cluster 集群详细步骤

1、环境准备 下载mysql集群版本&#xff0c;我这里下载的是mysql-cluster-8.0.39-winx64 https://dev.mysql.com/downloads/cluster/ 2、创建配置文件 mysql集群版本下载以后解压后目录如下&#xff0c;创建配置文件 config.ini(集群配置文件&#xff0c;my.ini mysql配置…

【大模型基础】P0 大模型之路 —— 窗外灯火阑珊

目录 前言 —— 本系列博文内容何谓语言语言、图形符号、编码与解码基于规则、基于统计 语言模型&#xff08;Language Model&#xff09;预训练语言模型BERT 与 GPT 大模型范式预训练 微调大模型提示 / 指令 OpenAI 若一个语言模型亮起一盏灯&#xff0c;你会发现&#xff0c…

三维布尔运算对不规范几何数据的兼容处理

1.前言 上一篇文章谈过八叉树布尔运算&#xff0c;对于规范几何数据的情况是没有问题的。 在实际情况中&#xff0c;由于几何数据来源不一&#xff0c;处理和生成方式不一&#xff0c;我们无法保证进行布尔运算的几何数据都是规范的&#xff0c;对于不规范情况有时候也有需求…

vue3写一个无限树形菜单,递归组件

原本使用element plus的el-tree&#xff0c;可是他的UI不匹配&#xff0c;狠难改成自己想要的&#xff0c;所以只能自己去写一个&#xff0c;做法&#xff1a;使用递归组件 效果 组件代码itemDir.vue // itemDir.vue<template><div><ul v-for"node in li…

【AcWing】852. spfa判断负环

#include<iostream> #include<algorithm> #include<cstring> #include<queue> using namespace std;const int N 1e510;int n,m; int h[N],w[N],e[N],ne[N],idx; int dist[N],cnt[N];//cnt存最短路径的边数 bool st[N];void add(int a,int b,int c){e[…

前端:Vue3学习-2

前端:Vue3学习-2 1. vue3 新特性-defineOptions2. vue3 新特性-defineModel3. vue3 Pinia-状态管理工具4. Pinia 持久化插件 -> pinia-plugin-persistedstate 1. vue3 新特性-defineOptions 如果要定义组件的name或其他自定义的属性&#xff0c;还是得回归原始得方法----再…

输送线相机拍照信号触发(博途PLC高速计数器中断立即输出应用)

博途PLC相关中断应用请参考下面文章链接: T法测速功能块 T法测速功能块(博途PLC上升沿中断应用)-CSDN博客文章浏览阅读165次。本文介绍了博途PLC中T法测速的原理和应用,包括如何开启上升沿中断、配置中断以及T法测速功能块的使用。重点讲述了在中断事件发生后执行的功能块处…

有希带你深入理解指针(4)

目录 前言&#x1f970;1.回调函数&#x1f63a;1.1回调函数的概念&#x1f60b; 2.qsort使用&#x1f92f;2.1什么是qsort&#x1f47b;2.2 qsort函数的使用&#x1f9d0; 3.模拟实现qsort&#x1f60e; 前言&#x1f970; 本篇文章是对指针知识的进一步讲解&#xff0c;如果…

【leetcode】二分查找专题

文章目录 1.基本的二分查找2.使用二分 查找左右端点2.1 左右端点2.2 二分模板 3.搜索插入位置4.x的平方根5.山脉数组的顶峰6.寻找峰值7.寻找旋转排序数组中的最小值 对于二分查找&#xff0c;相信大家都再熟悉不过了。一旦数据是有序的&#xff0c;我们大概率会想到二分&#x…

上海小学生古诗文大会2024年备考:吃透历年真题和知识点(持续)

一、小学生古诗文大会真题精选&#xff08;答案和解析见文末&#xff09; *1. 孟浩然的《宿建德江》是一首&#xff08;&#xff09;。 A.五言绝句 B.七言绝句 C.五言律诗 D.七言律诗 *2. 茕茕子立&#xff0c;形影相吊出自&#xff08;&#xff09; A.《出师表》 B.《…

常见Python GUI库分析

引言 在Python环境下进行桌面编程时&#xff0c;选择合适的GUI&#xff08;图形用户界面&#xff09;库至关重要。在Python环境下进行桌面编程GUI开发时&#xff0c;有多个优秀的库可供选择。以下是一些推荐的GUI库&#xff0c;包括它们的推荐理由、优劣势以及简单的demo示例。…

Deepspeed框架学习笔记

DeepSpeed 是由 Microsoft 开发的深度学习优化库,与PyTorch/TensorFlow等这种通用的深度学习框架不同的是,它是一个专门用于优化和加速大规模深度学习训练的工具,尤其是在处理大模型和分布式训练时表现出色。它不是一个独立的深度学习框架,而是依赖 PyTorch 等框架,扩展了…

C++20中lambda表达式新增加支持的features

1.弃用通过[]隐式捕获this&#xff0c;应使用[,this]或[,*this]显示捕获&#xff1a; namespace { struct Foo {int x{ 1 };void print(){//auto change1 [] { // badauto change1 [, this] { // good, this: referencethis->x 11;};change1();std::cout << "…

麒麟系统安装GPU驱动

1.nvidia 1.1显卡驱动 本机显卡型号:nvidia rtx 3090 1.1.1下载驱动 打开 https://www.nvidia.cn/geforce/drivers/ 也可以直接使用下面这个地址下载 https://www.nvidia.com/download/driverResults.aspx/205464/en-us/ 1.1.3安装驱动 右击&#xff0c;为run文件添加可…

【YOLO 系列】基于YOLOV8的智能花卉分类检测系统【python源码+Pyqt5界面+数据集+训练代码】

前言&#xff1a; 花朵作为自然界中的重要组成部分&#xff0c;不仅在生态学上具有重要意义&#xff0c;也在园艺、农业以及艺术领域中占有一席之地。随着图像识别技术的发展&#xff0c;自动化的花朵分类对于植物研究、生物多样性保护以及园艺爱好者来说变得越发重要。为了提…

接口自动化三大经典难题

目录 一、接口项目不生成token怎么解决关联问题 1. Session机制 2. 基于IP或设备ID的绑定 3. 使用OAuth或第三方认证 4. 利用隐式传递的参数 5. 基于时间戳的签名验证 二、接口测试中网络问题导致无法通过怎么办 1. 重试机制 2. 设置超时时间 3. 使用模拟数据 4. 网…

【STM32开发】GPIO最全解析及应用实例

目录 【1】GPIO概述 GPIO的基本概念 GPIO的应用 【2】GPIO功能描述 1.IO功能框图 2.知识补充 3.功能详述 浮空输入 上拉输入 下拉输入 模拟输入 推挽输出 开漏输出 复用开漏输出和复用推挽输出 【3】GPIO常用寄存器 相关寄存器介绍 4个32位配置寄存器 2个32位数据寄存器 1个32位…