1.重采样判断

上一章讲述了amcl中如何根据激光观测更新粒子权重,当粒子更新完后amcl会需要根据程序判断是否需要进行重采样。这个判断在粒子观测更新权重后进行判断,代码在amcl_node.cpp中:

    if(!(++resample_count_ % resample_interval_)){ pf_update_resample(pf_);resampled = true;}

这里通过两个参数判断是否进行重采样操作,resample_count_是个计数值,每次自加1,resample_interval_是一个常系数参数,可以在.launch文件中设置。这个值默认为1,所以如果不修改的话算法每一次运算都会进行重采样操作。

2.重采样原理

关于amcl中的重采样原理,这里一篇文章讲述的比较通俗易懂:《粒子滤波重采样的理解及MATLAB实现》。粒子滤波中重采样的过程其实就是相当于一个转转盘的过程,如下图所示:

这个转盘被分成了N分,即从w1到wn,和为1。每个区间就是一个粒子的权值,权值越大,区间的弧度就越大。每次都从w1左边的竖线开始转,显而易见,转到w1的概率为0-w1之间的随机数,转到w2的概率为w1到w1+w2之间的随机数,转到w3的概率为w1+w2到w1+w2+w3之间的随机数,后面的依次类推。当某个粒子的权值较大的时候,产生的随机数落在相应区间的概率就会增大,从而实现了对权值大粒子的多次复制,权值小的粒子的剔除。这样重采样的过程中不是全部复制大权值粒子,也有可能对小权值粒子进行了复制,因为区间虽小,但也有可能转到这个区间。不过这样在一定程度上也保证了粒子的多样性。

3.重采样代码实现

amcl中重采样的代码实际是在pf.c中:

void pf_update_resample(pf_t *pf)
{int i;double total;pf_sample_set_t *set_a, *set_b;pf_sample_t *sample_a, *sample_b;//double r,c,U;//int m;//double count_inv;double* c;double w_diff;set_a = pf->sets + pf->current_set;//current,只有0和1,a和b交替指向sets的第一个和第二个位置,//每个周期由a生成b,但是a所指向的sets的位置不一样,a在本个周期指向的是上个周期中b的位置set_b = pf->sets + (pf->current_set + 1) % 2;//这里是粒子集指针的切换//set_a为上周期采样的粒子,set_b为本周期将要采样的粒子if (pf->selective_resampling != 0){if (set_a->n_effective > 0.5*(set_a->sample_count)){// copy set a to bcopy_set(set_a,set_b);// Re-compute cluster statisticspf_cluster_stats(pf, set_b);// Use the newly created sample setpf->current_set = (pf->current_set + 1) % 2;return;}}// Build up cumulative probability table for resampling.累积概率以进行重采样// TODO: Replace this with a more efficient procedure   这里应该有更高效的方法// (e.g., http://www.network-theory.co.uk/docs/gslref/GeneralDiscreteDistributions.html)// 然后对粒子的权重进行积分,获得分布函数,后面用于生成样本。c = (double*)malloc(sizeof(double)*(set_a->sample_count+1));//malloc函数用于分配内存c[0] = 0.0;for(i=0;i<set_a->sample_count;i++)//权重累积c[i+1] = c[i]+set_a->samples[i].weight;// Create the kd tree for adaptive samplingpf_kdtree_clear(set_b->kdtree);//为自适应采样创建kd树// Draw samples from set a to create set b.total = 0;set_b->sample_count = 0;w_diff = 1.0 - pf->w_fast / pf->w_slow;// 此时和初始的分布差异较大,即w_idff变大,需要重采样;如果是大部分粒子有较大的权重,差异不大,不进行重采样if(w_diff < 0.0)w_diff = 0.0;//printf("w_diff: %9.6f\n", w_diff);// Can't (easily) combine low-variance sampler with KLD adaptive// sampling, so we'll take the more traditional route./*//不能容易地将低方差采样器与KLD自适应组合采样,所以我们采取更传统的路线。// Low-variance resampler, taken from Probabilistic Robotics, p110count_inv = 1.0/set_a->sample_count;r = drand48() * count_inv;c = set_a->samples[0].weight;i = 0;m = 0;*/while(set_b->sample_count < pf->max_samples){//set_b的粒子指针指向set_b的采样数之后的位置,用于添加重采样的粒子sample_b = set_b->samples + set_b->sample_count++;//0-1之间均匀分布的随机数,按照概率增加,w_diff越大,增加粒子的可能性也越大if(drand48() < w_diff)sample_b->pose = (pf->random_pose_fn)(pf->random_pose_data);//增加随机分布粒子,重采样else{// Can't (easily) combine low-variance sampler with KLD adaptive// sampling, so we'll take the more traditional route./*// Low-variance resampler, taken from Probabilistic Robotics, p110U = r + m * count_inv;while(U>c){i++;// Handle wrap-around by resetting counters and picking a new random// numberif(i >= set_a->sample_count){r = drand48() * count_inv;c = set_a->samples[0].weight;i = 0;m = 0;U = r + m * count_inv;continue;}c += set_a->samples[i].weight;}m++;*/// Naive discrete event sampler  离散采样器double r; //生成一个随机数r = drand48();//判断这个随机数处于哪个粒子周围,这里生成出一个随机数,当一个粒子权重越大,则其在0-1之间占用的比例越大,随机数取到这个粒子占用部分的概率越大for(i=0;i<set_a->sample_count;i++){if((c[i] <= r) && (r < c[i+1]))//将随机数以权重为概率分配到某处break;}//assert其作用是如果它的条件返回错误,则终止程序执行assert(i<set_a->sample_count);sample_a = set_a->samples + i;assert(sample_a->weight > 0);// Add sample to listsample_b->pose = sample_a->pose;//在一个即生成随机数位置增加一个粒子,某个位置附近粒子数越多或者权重越大,这个位置生成重采样的粒子概率越大}sample_b->weight = 1.0;//每个粒子都是1,之后标准化total += sample_b->weight;// Add sample to histogrampf_kdtree_insert(set_b->kdtree, sample_b->pose, sample_b->weight);//将样本添加到直方图,关于位姿的二叉树// See if we have enough samples yetif (set_b->sample_count > pf_resample_limit(pf, set_b->kdtree->leaf_count))break;}// Reset averages, to avoid spiraling off into complete randomness.if(w_diff > 0.0)//增加粒子集后重置似然pf->w_slow = pf->w_fast = 0.0;//fprintf(stderr, "\n\n");// Normalize weightsfor (i = 0; i < set_b->sample_count; i++){sample_b = set_b->samples + i;sample_b->weight /= total;//重采样以后每个粒子权重=1 / M}// Re-compute cluster statistics// 聚类,得到均值和方差等信息,将相近的一堆粒子融合成一个粒子pf_cluster_stats(pf, set_b);// Use the newly created sample set// 新粒子集pf->current_set = (pf->current_set + 1) % 2; //计算滤波器是否收敛pf_update_converged(pf);free(c);return;
}

代码中的:

  c = (double*)malloc(sizeof(double)*(set_a->sample_count+1));//malloc函数用于分配内存c[0] = 0.0;for(i=0;i<set_a->sample_count;i++)//权重累积c[i+1] = c[i]+set_a->samples[i].weight;

代表的就是标题2中的圆盘的产生过程,将每一个点的权重值放到一个对应的数组内,根据数组的值就可以知道当前处于哪个粒子。

然后下面的while循环实际上实现了粒子的重采样过程:

while(set_b->sample_count < pf->max_samples){//set_b的粒子指针指向set_b的采样数之后的位置,用于添加重采样的粒子sample_b = set_b->samples + set_b->sample_count++;//0-1之间均匀分布的随机数,按照概率增加,w_diff越大,增加粒子的可能性也越大if(drand48() < w_diff)sample_b->pose = (pf->random_pose_fn)(pf->random_pose_data);//增加随机分布粒子,重采样else{// Naive discrete event sampler  离散采样器double r; //生成一个随机数r = drand48();//判断这个随机数处于哪个粒子周围,这里生成出一个随机数,当一个粒子权重越大,则其在0-1之间占用的比例越大,随机数取到这个粒子占用部分的概率越大for(i=0;i<set_a->sample_count;i++){if((c[i] <= r) && (r < c[i+1]))//将随机数以权重为概率分配到某处break;}//assert其作用是如果它的条件返回错误,则终止程序执行assert(i<set_a->sample_count);sample_a = set_a->samples + i;assert(sample_a->weight > 0);// Add sample to listsample_b->pose = sample_a->pose;//在一个即生成随机数位置增加一个粒子,某个位置附近粒子数越多或者权重越大,这个位置生成重采样的粒子概率越大}sample_b->weight = 1.0;//每个粒子都是1,之后标准化total += sample_b->weight;// Add sample to histogrampf_kdtree_insert(set_b->kdtree, sample_b->pose, sample_b->weight);//将样本添加到直方图,关于位姿的二叉树// See if we have enough samples yetif (set_b->sample_count > pf_resample_limit(pf, set_b->kdtree->leaf_count))break;}

首先while循环中的第一个drand48()用于判断是否重采样粒子,这里用到了两个参数:pf->w_fast 与pf->w_slow。这两个参数代表了粒子的衰减率,当:

w_diff = 1.0 - pf->w_fast / pf->w_slow;

得到的值越大,说明粒子的权重越小,当前粒子的可信度比较小,需要重采样粒子。而w_diff的值越小,说明大部分粒子有较大的权重,差异不大,不进行重采样。

首先算法随机生成一个随机数,然后根据该随机数判断是否进行随机粒子生成。对于需要重新生成粒子的情况:

if(drand48() < w_diff)sample_b->pose = (pf->random_pose_fn)(pf->random_pose_data);//增加随机分布粒子,重采样

这里的位姿生成使用的是随机位姿生成的方式得到一个之前不存在的位姿,通过这种方式提高粒子的多样性。同时我们也可以看到,w_diff越小,重新生成随机粒子的概率也越小。

而对于不重新生成粒子的情况,这里用到了第二个drand48(),这个drand48()的作用就是前面提到的轮盘,它随机转到某一个权重范围下,然后根据这个权重范围获取对应的位姿。这样子权重大的粒子被复制的概率会高于权重低的粒子。当然这里虽然复制的位姿可能会是一样的,但是没有关系,因为里程计更新时携带了噪声系数,所以一次更新后这些一样的位姿就产生了变化。

最后当粒子的数量满足了一定要求后,整个重采样的过程就结束了。可以看出,在整个重采样的过程中,粒子进行了两次种情况的刷新,一种是随机生成的新的粒子,一种是对原来的粒子的复制。当粒子的权重越低,生成新的粒子的可能性就越大,粒子的权重越高,被复制的可能性也就越大。

AMCL代码详解(六)amcl中的重采样相关推荐

  1. maskrcnn_benchmark 代码详解(更新中...)

    前言: maskrcnn_benchmark是faceboock公司编写的一套用于目标检索的框架,该框架集成了目前用到的大部分使用深度卷积网络来进行目标检测的模型,其中包括Fast RCNN, Fas ...

  2. AMCL代码详解(七)amcl中的kd-Tree

    1.kd-Tree基础 kd树(k-dimensional树的简称),是一种分割k维数据空间的数据结构,主要应用于多维空间关键数据的近邻查找(Nearest Neighbor)和近似最近邻查找(App ...

  3. AMCL代码详解(五)根据激光观测更新粒子权重

    上一篇中讲述了里程计在amcl中的作用,算法根据里程计反馈对每个粒子的位姿进行更新.在粒子更新后,则需要根据激光的观测数据对粒子的权重进行更新,这里主要看一下激光电云在amcl中的作用: 1.激光回调 ...

  4. AMCL代码详解(三)创建粒子模型

    AMCL中创建粒子模型的方式有两种: 1.高斯分布模型 第一种方式的模型主要使用了高斯模型去建立了一系列粒子点: // // 使用高斯模型(均值,协方差)初始化滤波器 void pf_init(pf_ ...

  5. mysql 事务排他锁_[数据库事务与锁]详解六: MySQL中的共享锁与排他锁

    注明: 本文转载自http://www.hollischuang.com/archives/923 在MySQL中的行级锁,表级锁,页级锁中介绍过,行级锁是Mysql中锁定粒度最细的一种锁,行级锁能大 ...

  6. PX4飞控中利用EKF估计姿态角代码详解

    PX4飞控中利用EKF估计姿态角代码详解 PX4飞控中主要用EKF算法来估计飞行器三轴姿态角,具体c文件在px4\Firmware\src\modules\attitude_estimator_ekf ...

  7. python批量删除文件名中的下划线-代码详解

    删除文件名中的下划线 代码示例: 代码详解 对于文件名中出现的"_",如(500_600),将下划线消除,替换为(500600) 代码示例: import osdef rename ...

  8. Make your own Neural NetWork之代码详解中

    这篇博客接上一篇博客Make Your Own Neural Network之代码详解上.本文也是出自Make your own Neural NetWork这本书.上一篇博客讲了神经网络类的功能模块 ...

  9. html5代码转换为视频,HTML5中的视频代码详解

    摘要 腾兴网为您分享:HTML5中的视频代码详解,智学网,云闪付,易推广,小红书等软件知识,以及360win10,流量魔盒,fitbit,上港商城,安卓2.3.7,全民惠,五年级下册英语单词表图片,t ...

最新文章

  1. 北大博士干了半年外卖骑手,写出 AI 伦理论文登上顶刊,“系统知道一切”
  2. 电脑修改ip地址方法cmd_Mac系统如何创建用户群组?苹果电脑修改用户名方法
  3. 关于ZIP大文件压缩
  4. Yabbly:让经验缔结因果
  5. Java多线程初学者指南(12):使用Synchronized块同步变量
  6. POJ 2240 Arbitrage(判正环)
  7. php serialize和json_encode哪个更快_世界 10 大编程语言,Java 不是第一,PHP 才第五...
  8. Android网络请求通信之Volley
  9. android 判断资源是否使用6,android-download-manager – Android 6获取下载文件的路径
  10. php xml扩展,php-SimpleXML,请不要扩展实体
  11. sqlserver去重记录_SQL去除重复记录(七种)
  12. 【旧文章搬运】PsVoid中IrpCreateFile函数在Win7下蓝屏BUG分析及解决
  13. linux安装svn服务器的两种方式(转载)
  14. ideaIU-2020.1安装步骤
  15. 数据挖掘——关联分析Apriori算法
  16. docker参数详解
  17. oracle 判断数字是否连续,oracle中 如何 判断 表中字段是否为 连续的数字,例如‘3333333333',‘44444444’...
  18. STI解读A:STI测量方法
  19. 1967年图灵奖得主- 莫里斯·威尔克斯 (Maurice V. Wilkes)
  20. 【信息学奥赛一本通】1134:合法C标识符查

热门文章

  1. 常见影响接地电阻检测的因素及检测建议
  2. php json decode 多维,PHP Json_decode多维数组
  3. AI 让观众成为 3D 版《老友记》的导演了?
  4. delve应该安装到哪_golang调试工具Delve
  5. 基于element-ui插件设置页脚
  6. 【算法学习】三 选择排序分析
  7. 如何提高社交能力?情商是第一位!
  8. C语言学习之路(基础篇)—— 文件操作(下)
  9. 《数据挖掘(完整版)》笔记——基于规则的分类器
  10. 申请美国计算机科学,如何申请美国计算机科学专业