粒子滤波器的Matlab实现_matlab 粒子滤波器_木独的博客-程序员秘密

技术标签: 机器人  算法原理  matlab实现  粒子滤波器  状态估计  定位算法  

前言:粒子滤波器相较于卡尔曼滤波器或者UKF无迹卡尔曼滤波器而言,可以表达强非线性的变换且无需假设后验分布为高斯分布,在描述多峰分布时具有非常大的优势。粒子滤波器被广泛的应用于机器人系统中,如著名的Gmapping算法便是在粒子滤波器的基础上实现的,但是当前网络中对粒子滤波器的描述往往浅尝则止或长篇大论,导致学习起来往往是了解大概流程而不懂实际代码实现,无法应用于自己机器人中或困于理论推导。因此本文将简要的说明粒子滤波器的流程,然后在matlab中一步步的实现粒子滤波器,其中核心代码仅有20行,非常便于理解从而打通由理论到实际的过程。

原理介绍

算法流程

  1. 根据上一次的粒子分布和控制命令更新粒子位置
  2. 对更新后的粒子计算在该粒子的情况下,获得传感器观测的概率作为权重
  3. 根据权重进行重采样 

一、根据上一次分布和控制指令更新粒子位置

假设某一粒子位置为x = \left[ x,y,\theta \right]^T,控制命令为u_t= \begin{bmatrix} \theta & L \end{bmatrix},即旋转一定角度后前进一定的距离。跟新粒子的位置如下

x = x + R*u_t + rand

%控制机器人前进
function pos = move(robot_pos,set_w,set_v,control_noise)
    % 先进行旋转,加上噪声,并归一化到0~pi*2 
    pos(3) = mod(robot_pos(3) + set_w + control_noise*rand(),2*pi);
    % 前进距离+噪声
    V = set_v + control_noise*rand();
    % 坐标变换,因为前进距离是车体坐标系而要求的是世界坐标系下的
    pos(1) = mod(robot_pos(1) - V*sin(pos(3)),200); 
    pos(2) = mod(robot_pos(2) + V*cos(pos(3)),200); 
end

二、对更新后的粒子计算在该粒子的情况下,获得传感器观测的概率作为权重

计算权重的公式为

p=\eta p(z_t|x_t)

对于某一时刻真实的测量值为Z_t = \begin{bmatrix} z_1& z_2 & \cdots&z_n \end{bmatrix}^T,而对于其中一个粒子假设也进行了一次同样的测量根据地图估计出测量值为Z_t^i = \begin{bmatrix} z_1^i& z_2^i & \cdots&z_n^i \end{bmatrix}^T,则认为传感器的测量噪声是满足高斯分布的,因此该粒子测量值的概率分布为

P(z_t|x_t) = \eta exp\left(-\frac{1}{2}(x-\mu)^T \Sigma^{-1} (x-\mu)\right)

其中x带入传感器测量的真实值,而\mu为当前粒子根据地图信息估计出最可能的测量值,而协方差矩阵为测量传感器的设定的误差。

% 获取机器人在当前位置观测到对应数据的概率
function weight = get_probability(P,Z,landMark,sensor_noise)
    particle_Z = getDistance(P,landMark,sensor_noise);%根据地图获取理论上最有可能的观测结果
    distance = particle_Z - Z;%计算和真实值之间的差值
    noise = zeros([1,12]) + sensor_noise*sensor_noise;%根据设定观测噪声,构建协方差
    sigma = diag(noise);
    %带入多维高斯分布的公式中,这里去掉了归一化常数项
    weight = exp(-1/2 * (distance)' / (sigma) * (distance));
end

三、根据权重进行重采样 

使用如下的重采样策略,假设由M个样本,每个样本的权重为w_i \in R > 0,则根据权重进行归一化

\beta_i =\frac{w_i}{\sum_{i=m}^{M} w_i}

将0~1的圆弧分成M份,每一份的长度为\beta_i,然后在该圆上随机取值,落到哪一段则选取该段对应的粒子,如图所示当前落在2区域内,然后随机生成前进距离,然后根据随机距离进入了第四个粒子的范围则选中第四个粒子,然后不断重复直至找到M个粒子。

    % 粒子滤波:根据权重进行重采样
    sum_weight = sum(weight);
    weight = weight./sum_weight;%归一化权重
    max_weight = max(weight);
    index = ceil(rand()*particle_num);%开始时随机选取一个位置
    for i = 1:particle_num %随机采样的过程
        beta = rand() * 2 * max_weight;%随机生成前进距离,避免太远处或太近,设置为最大值的两倍
        while beta > weight(index+1)%重采样过程
            beta = beta - weight(index+1);
            index = mod(index + 1,particle_num-1);
        end
        temp_particle(:,i) = particle(:,index+1);
    end
    particle = temp_particle;

Matlab代码实现

%% begin
clc;
clear;
%% set param 
map_length = 200;%场地范围为200m
% 路标
landMarkNum = 12;%设定12个路标
%随机生成路标作为地图
landMark = [rand()*map_length rand()*map_length rand()*map_length rand()*map_length rand()*map_length rand()*map_length rand()*map_length rand()*map_length rand()*map_length rand()*map_length rand()*map_length rand()*map_length
            rand()*map_length rand()*map_length rand()*map_length rand()*map_length rand()*map_length rand()*map_length rand()*map_length rand()*map_length rand()*map_length rand()*map_length rand()*map_length rand()*map_length];
control_noise = 5; %控制机器人运动的噪声
sensor_noise = 10; %传感器对地图的测量噪声
robot_v = 20; %机器人在20m内随机运动
robot_w = 0;%0.1*pi;
T = 20;%进行T次迭代 
particle_num = 1000;%使用1000个粒子
particle = zeros([3,particle_num]);%粒子的位置
temp_particle = zeros([3,particle_num]);%更新粒子时的临时变量
weight = zeros([1,particle_num]);%粒子的权重
%% 粒子滤波步骤         
% 随机生成初始机器人的位置
robot_pos = [rand()*map_length,rand()*map_length,rand()*2*pi]';
% 随机生成初始粒子分布的位置
for i = 1:particle_num
    particle(:,i) = [rand()*map_length,rand()*map_length,rand()*2*pi]';
end
%% plot
plot(robot_pos(1),robot_pos(2),'b*');
xlim([0 200])
ylim([0 200])
hold on;
for i=1:landMarkNum
     plot(landMark(1,i),landMark(2,i),'go');
     hold on;
end
for i=1:particle_num
	plot(particle(1,i),particle(2,i),'y.');
	hold on;
end
for t = 1:T
    % 假设机器人一直在随机运动
    set_v = rand()*robot_v;
    set_w = rand()*robot_w;
    % 将命令给实际的机器人,但是会有一定的误差
    robot_pos = move(robot_pos,set_w,set_v,control_noise/10);
    % 在当前位置测量,测量同样会有一定误差
    Z = getDistance(robot_pos,landMark,sensor_noise);
    % 粒子滤波:根据上一次的分布和当前指令得到预测分布,并计算权重
    for i = 1:particle_num
        particle(:,i) = move(particle(:,i),set_w,set_v,control_noise);
        weight(i) = get_probability(particle(:,i),Z,landMark,sensor_noise);
    end
    % 粒子滤波:根据权重进行重采样
    sum_weight = sum(weight);
    weight = weight./sum_weight;%归一化权重
    max_weight = max(weight);
    index = ceil(rand()*particle_num);%开始时随机选取一个位置
    for i = 1:particle_num %随机采样的过程
        beta = rand() * 2 * max_weight;%随机生成前进距离,避免太远处或太近,设置为最大值的两倍内
        while beta > weight(index+1)%重采样过程
            beta = beta - weight(index+1);
            index = mod(index + 1,particle_num-1);
        end
        temp_particle(:,i) = particle(:,index+1);
    end
    particle = temp_particle;
    %% update plot
    clf;
    for i=1:particle_num
        plot(particle(1,i),particle(2,i),'r.');
        hold on;
    end
    plot(robot_pos(1),robot_pos(2),'b*');
    xlim([0 200])
    ylim([0 200])
    hold on;
    pause(0.1);
end
     
%% function define
% 获取机器人在当前位置观测到对应数据的概率
function weight = get_probability(P,Z,landMark,sensor_noise)
    particle_Z = getDistance(P,landMark,sensor_noise);%根据地图获取理论上最有可能的观测结果
    distance = particle_Z - Z;%计算和真实值之间的差值
    noise = zeros([1,12]) + sensor_noise*sensor_noise;%根据设定观测噪声,构建协方差
    sigma = diag(noise);
    %带入多维高斯分布的公式中,这里去掉了归一化常数项
    weight = exp(-1/2 * (distance)' / (sigma) * (distance));
end
%计算当前机器人位置观测到地图中的地标位置的距离
function distance = getDistance(position,landMark,sensor_noise)
    dis = [0,0,0,0,0,0,0,0,0,0,0,0]';
    for j=1:length(landMark) %遍历所有地标
        % 计算和当前机器人的距离
        l = sqrt((position(1) - landMark(1,j))^2 + (position(2) - landMark(2,j))^2);
        dis(j) = l + sensor_noise*rand();% 加上随机生成的噪声项
    end
    % 返回各距离数据
    distance = dis;
end

%控制机器人前进
function pos = move(robot_pos,set_w,set_v,control_noise)
    % 先进行旋转,加上噪声,并归一化到0~pi*2 
    pos(3) = mod(robot_pos(3) + set_w + control_noise*rand(),2*pi);
    % 前进距离+噪声
    V = set_v + control_noise*rand();
    % 坐标变换,因为前进距离是车体坐标系而要求的是世界坐标系下的
    pos(1) = mod(robot_pos(1) - V*sin(pos(3)),200); 
    pos(2) = mod(robot_pos(2) + V*cos(pos(3)),200); 
end

  

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/RoboChengzi/article/details/105905680

智能推荐

[IE编程] 如何获得IE版本号_webbrowser获取ie的版本_云深互联的博客-程序员秘密

程序中使用IE的WebBrowser 控件(MSHTML)或者开发BHO插件的时候,通常需要获得IE的版本号以便做不同处理。这里介绍2中获得IE版本号的方法: 1) 通过注册表IE的版本号信息放在注册表的: HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Internet Explorer/Version   注:64位机器的32位IE在 H

[IE编程] IE 版本号大全_云深互联的博客-程序员秘密

IE 所有的历史版本的版本号都可以在这里查到: http://support.microsoft.com/kb/164539  最近的 IE8 RC1 的版本号(8.0.6001.18372) 还没有加进去。 Version          Product   ------------------------------------------------------------

去IOE(IBM服务器,Oracle,EMC存储),Oracle迁移postgreSQL部分问题解决方案_去dell ibm oracle_dzg红茶的博客-程序员秘密

Oracle迁移postgreSQL部分问题解决方案 “去IOE化"理念最早是由阿里巴巴提出。具体就是要把公司里IBM服务器、Oracle数据库、EMC存储都替换掉, 更换为开源或国产化的选项,postgreSQL 对Oracle兼容性比较好。1.当前时间 oracle的sysdate处理方法:未开启事务时用now()替换,一般项目也是没有事务的。在事务中用到sysdate时,建议用clock_timestamp()替换。例子select sysdate from dual 等价于

linux编程 - 给线程起名字_落尘纷扰的博客-程序员秘密

通过prctl给线程起名为了能方便的区分一个进程中的每个线程,可以通过prctl()给每个线程取个名字。这样在会创建多个线程的程序执行过程中,就能知道一个pid或tid对应的是哪个线程。prctl是个系统调用,可以用来读取和更改一个线程的属性。其用户态接口定义如下:#include int prctl(int option, unsigned long arg2, unsigned

awstats分析nginx日志文件_chlinwei的博客-程序员秘密

awstats分析nginx日志文件,将生成的结果(为txt文件)保存在/var/www/awstats目录下,通过apche来显示生成的结果。nginx的域名为:www.a.com:80 LogFile="/usr/local/mybin/nginx/logs/access.log"  #nginx的日志文件路径DirData="/var/www/awstats/"

drop index if exists mysql_mysql判断索引是否存在时删除索引的方法(mysql drop index if exists PROCEDURE)..._鹊华清扬的博客-程序员秘密

mysql判断索引是否存在时删除索引的方法(mysql drop index if exists PROCEDURE)mysql的drop index语句不支持if exists条件,在sql中先删除索引,再创建索引,如果对于新建的数据库,库中没有该索引,就会报错,导致后面的sql不再执行。因此需要使用存储过程来判断索引是否存在,如果存在则删除。sql代码如下:Sql代码DROP PROCEDUR...

随便推点

qcc514x-qcc304x调试笔记-charger_leezs0614的博客-程序员秘密

adk\src\domains\power\charger_monitor\charger_monitor_config.h/*! The time to debounce charger state changes (ms). The charger hardware will have a more limited range. */#define appConfigChargerStateChangeDebounce() (128)/*! Trickle-cha

博弈之~取石子系列(三) nyoj 137_123逆风飞翔的博客-程序员秘密

取石子(三)时间限制:1000 ms  |  内存限制:10000 KB难度:6描述小王喜欢与同事玩一些小游戏,今天他们选择了玩取石子。游戏规则如下:共有N堆石子,已知每堆中石子的数量,两个人轮流取子,每次只能选择N堆石子中的一堆,取一定数量的石子(最少取一个),取过子之后,还可以将该堆石子中剩下的任意多个石子中随意选取几个放到其它的任意一堆或几堆上。等哪个人无法取子时就表示此人输掉了游戏。注意,...

Leecode 2104.子数组范围和题解_leetcode 2104_种志强的博客-程序员秘密

利用最简单双指针方法进行子数组范围和的求解

获取某个输入框的字符长度_JQuery获取文本框中字符长度的代码_熊天然的博客-程序员秘密

趁我写例子这点时间有两个人回答了 呵呵刚才试验过了 看下例子吧JS 方法:// 得到字符串的真实长度(双字节换算为两个单字节)function getStrActualLen(sChars){//sChars.replace(/[^\x00-\xff]/g,"xx").length/1024+"字节";//Math.round(sChars.replace(/[^\x00-\xff]/g,"xx"...

pygame 安装_baimi1963的博客-程序员秘密

1.https://bitbucket.org/pygame/pygame/downloads/ 确认自己需要的版本2. win 用管理员账号运行 cmd py -m pip install -U pygame --userpygame-1.9.2-cp35-cp35m-macosx_10_9_intel.whl (自动下的比我看到的还新)...

zTree -- jQuery 树插件的使用_mengmengdastyle的博客-程序员秘密

官网地址:http://www.treejs.cn/v3/main.php#_zTreeInfo一、介绍zTree 是一个依靠 jQuery 实现的多功能 “树插件”。优异的性能、灵活的配置、多种功能的组合是 zTree 最大优点。特点:zTree v3.0 将核心代码按照功能进行了分割,不需要的代码可以不用加载采用了 延迟加载 技术,上万节点轻松加载,即使在 IE6 下也...

推荐文章

热门文章

相关标签