AMCL代码详解(四)根据运动更新粒子位姿

本文解析了AMCL(Adaptive Monte Carlo Localization)中粒子滤波器的运动更新过程,重点讲解了UpdateAction函数如何根据接收到的激光点云和里程计数据,通过运动分解和噪声模型更新粒子集合。详细介绍了如何处理位姿变化、噪声计算及应用到粒子位姿更新的步骤。
摘要由CSDN通过智能技术生成

AMCL中粒子的运动更新函数在AmclNode::laserReceived函数中进行调用,当接收到正常的激光点云后调用里程计更新函数对每个粒子的位姿进行运动更新,具体实现是在amcl_odom.cpp函数中的AMCLOdom::UpdateAction:


// Apply the action model
// pf是需要更新的滤波器对象,data则是指示了更新的运动数据。
bool AMCLOdom::UpdateAction(pf_t *pf, AMCLSensorData *data)
{
  //通过强制类型转换,从输入参数data中获取运动数据对象。
  //AMCLOdomData包含了一个三维的pose以及三维的delta
  //pose里面存放的应该是上一时刻的位姿xy以及角度,delta里面存放的是上一时刻到当前时刻的xy位移以及角度变化量
  AMCLOdomData *ndata;
  ndata = (AMCLOdomData*) data;

  // Compute the new sample poses
  // 通过pf模块的函数pf_vector_sub,从运动数据中推算出上一时刻的里程计位姿。 此外,还通过临时变量set从里程计对象中获取新的粒子集合。
  pf_sample_set_t *set;

  set = pf->sets + pf->current_set;
  pf_vector_t old_pose = pf_vector_sub(ndata->pose, ndata->delta);
  // 根据模型类型更新粒子集合。
  switch( this->model_type )
  {
  //里程计运动模型可以参看这个https://gaoyichao.com/Xiaotu/?book=probabilistic_robotics&title=pr_chapter5#%E7%AE%97%E6%B3%955.6
  case ODOM_MODEL_DIFF:
  {
    // Implement sample_motion_odometry (Prob Rob p 136)
    // 定义了一堆临时变量,它们都是更新过程中需要用到的中间变量
    double delta_rot1, delta_trans, delta_rot2;
    double delta_rot1_hat, delta_trans_hat, delta_rot2_hat;
    double delta_rot1_noise, delta_rot2_noise;

    // Avoid computing a bearing from two poses that are extremely near each
    // other (happens on in-place rotation).
    // 里程计模型中两个旋转和一个平移运动的计算
    // 运动分解中将A到B的过程分解为三部分:
    // 从当前机器人朝向旋转到AB点连线delta_rot1
    // 从A点直线行驶到B点delta_trans
    // 在B点旋转到达最终位姿delta_rot2
    if(sqrt(ndata->delta.v[1]*ndata->delta.v[1] + 
            ndata->delta.v[0]*ndata->delta.v[0]) < 0.01)
      delta_rot1 = 0.0;
    else
      delta_rot1 = angle_diff(atan2(ndata->delta.v[1], ndata->delta.v[0]),
                              old_pose.v[2]);
    delta_trans = sqrt(ndata->delta.v[0]*ndata->delta.v[0] +
                       ndata->delta.v[1]*ndata->delta.v[1]);
    delta_rot2 = angle_diff(ndata->delta.v[2], delta_rot1);

    // We want to treat backward and forward motion symmetrically for the
    // noise model to be applied below.  The standard model seems to assume
    // forward motion.
    // 计算两个旋转噪声
    delta_rot1_noise = std::min(fabs(angle_diff(delta_rot1,0.0)),
                                fabs(angle_diff(delta_rot1,M_PI)));
    delta_rot2_noise = std::min(fabs(angle_diff(delta_rot2,0.0)),
                                fabs(angle_diff(delta_rot2,M_PI)));
    //对粒子集合中的每个样本进行更新
    for (int i = 0; i < set->sample_count; i++)
    {
      pf_sample_t* sample = set->samples + i;

      // Sample pose differences
      //计算三次运动的旋转以及平移量,pf_ran_gaussian产生一个随机高斯分布的参数应用到运动过程
      delta_rot1_hat = angle_diff(delta_rot1,
                                  pf_ran_gaussian(this->alpha1*delta_rot1_noise*delta_rot1_noise +
                                                  this->alpha2*delta_trans*delta_trans));
      delta_trans_hat = delta_trans - 
              pf_ran_gaussian(this->alpha3*delta_trans*delta_trans +
                              this->alpha4*delta_rot1_noise*delta_rot1_noise +
                              this->alpha4*delta_rot2_noise*delta_rot2_noise);
      delta_rot2_hat = angle_diff(delta_rot2,
                                  pf_ran_gaussian(this->alpha1*delta_rot2_noise*delta_rot2_noise +
                                                  this->alpha2*delta_trans*delta_trans));

      // Apply sampled update to particle pose
      // 将位姿赋值给原来的pose,即更新位姿
      sample->pose.v[0] += delta_trans_hat * 
              cos(sample->pose.v[2] + delta_rot1_hat);
      sample->pose.v[1] += delta_trans_hat * 
              sin(sample->pose.v[2] + delta_rot1_hat);
      sample->pose.v[2] += delta_rot1_hat + delta_rot2_hat;
    }
  }
  break;
  }
  return true;
}

这个函数的传参给进来的是两个参数:需要更新的滤波器对象(pf)以及更新的运动数据(data)。这两个数据都在amcl_node.cc中处理。

其中data包含了上一时刻的位姿(pose)以及上一时刻到当前时刻的xy方向的位移分量和z方向的旋转角度

在UpdateAction这个函数中其实差不多就干了一件事:将pf的点的位姿根据里程计信息进行一次迭代,当然这个里程计信息在迭代的过程中是加了一定噪声的。主要步骤包含如下:

首先对机器人的运动进行分解,在这里机器人的运动(从A到B)可以分解为三个部分:

// 从当前机器人朝向旋转到AB点连线delta_rot1
// 从A点直线行驶到B点delta_trans
// 在B点旋转到达最终位姿delta_rot2
    if(sqrt(ndata->delta.v[1]*ndata->delta.v[1] + 
            ndata->delta.v[0]*ndata->delta.v[0]) < 0.01)
      delta_rot1 = 0.0;
    else
      delta_rot1 = angle_diff(atan2(ndata->delta.v[1], ndata->delta.v[0]),
                              old_pose.v[2]);
    delta_trans = sqrt(ndata->delta.v[0]*ndata->delta.v[0] +
                       ndata->delta.v[1]*ndata->delta.v[1]);
    delta_rot2 = angle_diff(ndata->delta.v[2], delta_rot1);

然后根据计算的角度生成两个高斯噪声:

    delta_rot1_noise = std::min(fabs(angle_diff(delta_rot1,0.0)),
                                fabs(angle_diff(delta_rot1,M_PI)));
    delta_rot2_noise = std::min(fabs(angle_diff(delta_rot2,0.0)),
                                fabs(angle_diff(delta_rot2,M_PI)));

最后根据位姿以及偏移量还有两个噪声值更新每个pf粒子的位姿:

    for (int i = 0; i < set->sample_count; i++)
    {
      pf_sample_t* sample = set->samples + i;

      // Sample pose differences
      //计算三次运动的旋转以及平移量,pf_ran_gaussian产生一个随机高斯分布的参数应用到运动过程
      delta_rot1_hat = angle_diff(delta_rot1,
                                  pf_ran_gaussian(this->alpha1*delta_rot1_noise*delta_rot1_noise +
                                                  this->alpha2*delta_trans*delta_trans));
      delta_trans_hat = delta_trans - 
              pf_ran_gaussian(this->alpha3*delta_trans*delta_trans +
                              this->alpha4*delta_rot1_noise*delta_rot1_noise +
                              this->alpha4*delta_rot2_noise*delta_rot2_noise);
      delta_rot2_hat = angle_diff(delta_rot2,
                                  pf_ran_gaussian(this->alpha1*delta_rot2_noise*delta_rot2_noise +
                                                  this->alpha2*delta_trans*delta_trans));

      // Apply sampled update to particle pose
      // 将位姿赋值给原来的pose,即更新位姿
      sample->pose.v[0] += delta_trans_hat * 
              cos(sample->pose.v[2] + delta_rot1_hat);
      sample->pose.v[1] += delta_trans_hat * 
              sin(sample->pose.v[2] + delta_rot1_hat);
      sample->pose.v[2] += delta_rot1_hat + delta_rot2_hat;
    }

大致的步骤就是分为以上三步,这里之所以不是简单的直接对每个粒子直接加上一个位姿的偏移量应该是考虑了里程计数据也不一定是完全准确的,所以需要增加一个高斯噪声,而高斯噪声的协方差应该是随时间或者速度变化的,所以先进行运动分解,根据分解的结果每次产生一个不完全相同的高斯噪声函数,最后再将含有噪声的位移变化传递给pf。

看到这里可以发现AMCL的噪声存在于两个地方,一个在于随机生成粒子时,需要添加一个噪声项以利于随机生成一系列粒子;另一个地方则是这里运动更新时,对里程计数据添加噪声以生成一系列不同情况的位姿。

根据引用\[2\]中提供的伪代码AMCL(自适应蒙特卡洛定位)中的粒子分布是通过运动采样模型生成的。具体流程如下: 1. 获取机器人当前位姿和移动增量delta。 2. 使用运动采样模型,在上一个位姿Xt-1和移动增量delta的基础上,按照给定的分布生成若干数量的粒子,即生成当前状态的可能位姿。 3. 对每个粒子使用激光传感器模型计算在其状态下获得测量Zt的概率,并计算总概率totalweight。 4. 根据粒子的权重进行采样,即根据粒子的权重值,按照一定的概率选择粒子。具体方法是生成一个(0,1)之间的随机数r,然后根据粒子的累积权重值c,选择满足条件c\[i\] <= r < c\[i+1\]的粒子作为采样结果。 5. 采样后粒子数量不变,并重置所有粒子的权重为平均值(1/sample_count)。 6. 对粒子进行聚类,计算每个聚类的位姿均值、权重均值、协方差等参数。 7. 选择平均权重最大的聚类作为最终的机器人位姿,并根据该位姿修正ODOM坐标系下机器人的位姿。 8. 完成一个AMCL粒子滤波周期。 综上所述,AMCL粒子分布是通过运动采样模型生成的,并根据粒子的权重进行采样和聚类,最终得到机器人的位姿估计。 #### 引用[.reference_title] - *1* *2* *3* [AMCL粒子滤波](https://blog.csdn.net/gadwgdsk/article/details/103364846)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一叶执念

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值