Gmapping源码阅读(2)————激光雷达消息回调函数

504 阅读3分钟

laserCallback消息回调函数

void SlamGMapping::laserCallback(const sensor_msgs::LaserScan::ConstPtr& scan)
{
  laser_count_++;
  if ((laser_count_ % throttle_scans_) != 0)
    return;

  static ros::Time last_map_update(0,0);

  // We can't initialize the mapper until we've got the first scan
  // (1) 判断激光是否为第一帧,当为第一帧时初始化地图,并修改标志位;
  if(!got_first_scan_)
  {
    if(!initMapper(*scan))  //initMapper 进行初始化参数
      return;
    got_first_scan_ = true;
  }

  GMapping::OrientedPoint odom_pose;//odom_pose事包含x,y,theta信息的结构体
  // (2)调用addScan()函数,添加激光数据, 若返回值为真执行(3);。
  if(addScan(*scan, odom_pose)) //核心算法
  {
    ROS_DEBUG("scan processed");
    // (3)获取(2)中处理后,粒子最佳匹配位置;
    GMapping::OrientedPoint mpose = gsp_->getParticles()[gsp_->getBestParticleIndex()].pose;//获取粒子滤波的最佳匹配
    ROS_DEBUG("new best pose: %.3f %.3f %.3f", mpose.x, mpose.y, mpose.theta);
    ROS_DEBUG("odom pose: %.3f %.3f %.3f", odom_pose.x, odom_pose.y, odom_pose.theta);
    ROS_DEBUG("correction: %.3f %.3f %.3f", mpose.x - odom_pose.x, mpose.y - odom_pose.y, mpose.theta - odom_pose.theta);
    // (4)将(3)中获取的位姿用于激光到地图、里程计到地图、地图到里程计 tf 变换;
    tf::Transform laser_to_map = tf::Transform(tf::createQuaternionFromRPY(0, 0, mpose.theta), tf::Vector3(mpose.x, mpose.y, 0.0)).inverse();    // 激光到地图tf变换
    tf::Transform odom_to_laser = tf::Transform(tf::createQuaternionFromRPY(0, 0, odom_pose.theta), tf::Vector3(odom_pose.x, odom_pose.y, 0.0)); // 里程计到地图tf比那混
    
    map_to_odom_mutex_.lock();  //线程锁->上锁
    map_to_odom_ = (odom_to_laser * laser_to_map).inverse();
    map_to_odom_mutex_.unlock(); // 线程锁->解锁

    if(!got_map_ || (scan->header.stamp - last_map_update) > map_update_interval_)//地图更新条件
    {
      updateMap(*scan);//对地图更新
      last_map_update = scan->header.stamp;
      ROS_DEBUG("Updated the map");
    }
  } else
    ROS_DEBUG("cannot process scan");
}

该函数传入激光雷达消息。

  1. 首先判断激光是否是第一帧,当为第一帧时初始化地图并修改标志位
  2. 调用addScan()返回激光数据,若返回值为真执行(3)
  3. 获取(2)中处理后的粒子最佳匹配位置
  4. 将(3)中获取的位姿用于激光到地图、里程计到地图、地图到里程计 tf 变换
  5. 执行地图更新

然后我们对每一步骤进行分析。

  • step1: 首先判断激光是否是第一帧,当为第一帧时初始化地图并修改标志位,这里有一个函数 initMapper(),在我们将整个函数看完后再详细解析。 这里初始化了一个变量odom_pose,包含x,y,theta等信息,用来存储里程计信息。
  • step2: 调用函数addScan()返回激光数据信息,若存在激光数据信息则执行后面的步骤。这里的addScan()函数也要放在后面详细的解析。
  • step3 定义变量mpose用来存储粒子滤波的最佳匹配,获取粒子滤波的最佳匹配通过变量 GMapping::GridSlamProcessor* gps_来获取,这个类也将在后面详解。
  • step4 获取激光到地图laser_to_map,里程计到激光odom_to_laser,地图到里程计map_to_odom的坐标变换。 laser_to_map通过粒子滤波估计的最佳位姿获取;odom_to_laser通过里程计信息获取;map_to_odom则通过前两者的乘机获取。这里对获取map_to_odom加线程锁原因应该是防止其他线程对laser_to_mapodom_to_laser的更新导致map_to_odom数据的出错?是这样吗,还不太确定。
  • step5 然后就是对地图进行更新,地图更新的条件就是,获取到了地图,并且更新频率间隔位为5。然后就是更新地图的函数,这个函数也放在后面详细讲解。这里挖的坑有点多,后面就是慢慢填坑的时间。