一文理解粒子滤波

news2025/1/6 9:20:47

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.coloradmin.cn/o/2112960.html

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

相关文章

揭秘帕金森症幕后元凶:是哪些因素悄悄“震颤”了生活?

在这个快节奏的时代&#xff0c;健康成为了我们最宝贵的财富之一。然而&#xff0c;有一种名为“帕金森病”的神秘疾病&#xff0c;正悄悄影响着无数人的生活&#xff0c;让他们的日常充满了“不由自主”的颤抖。今天&#xff0c;就让我们一起揭开帕金森症的神秘面纱&#xff0…

【电路笔记】-同相运算放大器

同相运算放大器 文章目录 同相运算放大器1、概述2、理想的同相运算放大器3、实际同相运算放大器3.1 闭环增益3.2 输出阻抗3.3 输入阻抗4、同相运算放大器示例4.1 缓冲电路4.2 示例5、总结1、概述 施加到运算放大器的电压信号可以提供给其同相输入端 (+) 或反相输入端 (-)。 这…

ansible--role

简介 roles是ansible&#xff0c;playbooks的目录的组织结构&#xff0c;将代码或文件进行模块化&#xff0c;成为roles的文件目录组织结构。 易读&#xff0c;代码可冲哟美好&#xff0c;层次清晰 目录机构 mkdir roles/nginx/{files,handlers,tasks,templates,vars} -ptou…

LLM模型:代码讲解Transformer运行原理

视频讲解、获取源码&#xff1a;LLM模型&#xff1a;代码讲解Transformer运行原理(1)_哔哩哔哩_bilibili 1 训练保存模型文件 2 模型推理 3 推理代码 import torch import tiktoken from wutenglan_model import WutenglanModelimport pyttsx3# 设置设备为CUDA&#xff08;如果…

javaWeb【day04】--(MavenSpringBootWeb入门)

01. Maven课程介绍 1.1 课程安排 学习完前端Web开发技术后&#xff0c;我们即将开始学习后端Web开发技术。做为一名Java开发工程师&#xff0c;后端Web开发技术是我们学习的重点。 1.2 初识Maven 1.2.1 什么是Maven Maven是Apache旗下的一个开源项目&#xff0c;是一款用于…

数据治理:企业数字化转型的关键环节

数据治理&#xff1a;企业数字化转型的关键环节 前言数据治理&#xff1a;企业数字化转型的关键环节 前言 在当今数字化时代&#xff0c;企业的发展与数据紧密相连。数据已成为企业的重要资产&#xff0c;而如何有效地治理数据&#xff0c;使其发挥最大价值&#xff0c;成为企…

Python安装llama库出错“metadata-generation-failed”

Python安装llama库出错“metadata-generation-failed” 1. 安装llama库时出错2. 定位问题1. 去官网下载llama包 2.修改配置文件2.1 解压文件2.2 修改配置文件 3. 本地安装文件 1. 安装llama库时出错 2. 定位问题 根据查到的资料&#xff0c;发现时llama包中的execfile函数已经…

旋翼无人机的应用场景和用途!!!

1. 航拍摄影 全景拍摄&#xff1a;旋翼无人机可以携带摄像装置进行大规模航拍&#xff0c;广泛应用于影视制作、广告拍摄、城市规划、房地产宣传等领域。其独特的视角和高度&#xff0c;能够拍摄到地面难以捕捉的壮丽景色&#xff0c;为观众带来震撼的视觉效果。 测绘与地理信…

USB通信协议基础概念

文章目录 一、什么是USB1. **标准化接口**2. **热插拔**3. **即插即用**4. **电源供给**5. **数据传输速度**6. **连接类型**7. **协议和功能** 二、USB的三个部分1. **USB Host&#xff08;主机&#xff09;**2. **USB Device&#xff08;设备&#xff09;**3. **USB Hub&…

Ubuntu 22.04 make menuconfig 失败原因

先 安装一些配置 linux下使用menuconfig需要安装如下库_menuconfig 安装-CSDN博客 然后 cd 到指定源代码 需要在内核文件目录下编译 Linux 内核源码&#xff08;kernel source&#xff09;路径_--kernel-source-path-CSDN博客 make menuconfig 又报错 说是gcc 12什么什么&…

Linux概述、远程连接、常用命令

Linux介绍 Linux操作系统介绍 Linux操作系统的特点 开源免费安全稳定可移植性好 Linux可以安装在不同的设备上 高性能 Linux的使用领域 应用服务器数据库服务器网络服务器虚拟化云计算嵌入式领域个人PC移动手机 Linux文件系统和目录 /&#xff1a;根目录&#xff0c;唯一/h…

RT-Thread(Nano版本)的快速移植(基于NUCLEO-F446RE)

目录 概述 1 RT-Thread 1.1 RT-Thread的版本 1.2 认识Nano版本 2 STM32F446U上移植RT-Thread 2.1 STM32Cube创建工程 2.2 移植RT-Thread 2.2.1 安装RT-Thread Packet 2.2.2 加载RT-Thread 2.2.3 匹配相关接口 2.2.3.1 初次编译代码 2.2.3.2 匹配端口 2.2.4 移植FinSH…

基于飞腾平台的Hive的安装配置

【写在前面】 飞腾开发者平台是基于飞腾自身强大的技术基础和开放能力&#xff0c;聚合行业内优秀资源而打造的。该平台覆盖了操作系统、算法、数据库、安全、平台工具、虚拟化、存储、网络、固件等多个前沿技术领域&#xff0c;包含了应用使能套件、软件仓库、软件支持、软件适…

Matplotlib 颜色设置详解

在使用matplotlib进行颜色绘制的时候,如绘制图表、背景色或者对文字设置的时候都可以配置颜色, 以下说明主流的三种颜色使用方法 颜色名称 可以是直接使用颜色名称的字符串对color进行赋值,包括可以使用首字母缩写或者完整拼写的形式,以下为部分颜色的书写形式 缩写版 • …

机器学习课程学习周报十一

机器学习课程学习周报十一 文章目录 机器学习课程学习周报十一摘要Abstract一、机器学习部分1.1 再探GAN的数学原理1.1.1 似然与概率1.1.2 GAN和最大似然估计1.1.3 最大后验概率 1.2 WGAN1.3 GAN的性能评估方法1.4 条件型生成 总结 摘要 本周的学习主要围绕生成对抗网络&#…

详细介绍msvcr120.dll文件以及修复msvcr120.dll丢失的几种方法

遇到“msvcr120.dll丢失”错误通常会在尝试运行某些程序时发生&#xff0c;这类错误提示“程序无法启动&#xff0c;因为您的计算机缺少msvcr120.dll文件。”这可能导致许多用户感到困扰和不便。有几个步骤可以帮助你轻松修复“msvcr120.dll丢失”错误&#xff0c;保证应用程序…

Linux新建虚拟机Ubuntu详解

1. 打开软件&#xff0c;点击新建虚拟机&#xff1b; 2. 选择典型&#xff1b; 3. 点击稍后安装操作系统后&#xff0c;点击下一步&#xff1b; 4. 选择客户机操作系统以及版本&#xff0c;这里我们选择Ubuntu 64位&#xff1b; 5. 给虚拟机命名以及新建文件夹存放虚拟机位置&…

bat批处理实现从特定文件夹中提取文件内容并以父文件夹名存储

1、需求分析 标题是bat批处理实现从特定文件夹中提取文件内容并以父文件夹名存储。这里面我们要做的工作是&#xff1a; ①、批处理脚本使用的是bat文件&#xff1b; ②、文件夹下面有很多子文件夹&#xff0c;然后子文件夹下仍然有相同的文件结构&#xff0c;我们需要从三级…

创业者必读!选择拍卖源码还是自建开发,哪种方案更安全?

在当今数字化时代&#xff0c;拍卖平台作为一种独特的电子商务模式&#xff0c;正逐渐成为人们关注的焦点。随着互联网技术的发展&#xff0c;网络安全问题变得越来越突出。如何保障用户数据安全&#xff0c;防止信息泄露及攻击事件的发生&#xff0c;已经成为拍卖软件开发者面…

面试必备:接口自动化测试精选面试干货

一、 请问你是如何做接口测试的&#xff1f; 大体来说&#xff0c;经历以下过程&#xff1a;接口需求调研、接口测试工具选择、接口测试用例编写、接口测试执行、接口测试回归、接口测试自动化持续集成。具体来说&#xff0c;接口测试流程分成以下九步&#xff1a; 第一步&am…