【Apollo解读代码系列之perception】lidar模块tracker逻辑分析

lidar模块之tracker

  Apollo感知代码之激光雷达模块的追踪使用的是SORT算法架构。主要包括以下几个模块:关联模块association、通用模块common、测量值模块measurement、多激光雷达融合模块multi_lidar_fusion、地图分割semantic_map

lidar-tracker代码主要结构图

在这里插入图片描述
  其中MlfTrackData为追踪数据处理结构,继承TrackDataTrackedObject为输入的测量值结构,初始化都是base::ConcurrentObjectPool的序列容器存储。你可以理解TrackedObject就是输入的目标,MlfTrackData为追踪列表里面的一个目标物体,然后通过base::ConcurrentObjectPool来存储多个TrackedObjectMlfTrackData物体。

补:我们知道追踪列表维护一个数据结构就是上面的TrackData,测量值维护的数据结构是TrackObject。这里的MlfTrackData继承自TrackData主要是由于激光雷达有不同的品牌类型,所以会存在传感器类型的一个数据确认过程。具体细节请看代码部分吧,这里仅仅做个提示。

  上面为感知激光的追踪代码,MlfEngine继承接口类BaseMultiTargetTracker,其内部包含两个变量MlfTrackerMlfTrackObjectMatcherMlfTracker属于状态变量更新模块,MlfTrackObjectMatcher属于数据匹配模块,主要采取多种距离加权方式来设定关联权重,其中关联矩阵获取最终的匹配结果根据激光雷达障碍物是否为背景采取不同的方式。

void MlfTrackObjectMatcher::ComputeAssociateMatrix(
    const std::vector<MlfTrackDataPtr> &tracks,
    const std::vector<TrackedObjectPtr> &new_objects,
    common::SecureMat<float> *association_mat) {
  for (size_t i = 0; i < tracks.size(); ++i) {
    for (size_t j = 0; j < new_objects.size(); ++j) {
      // 计算关联的权重
      (*association_mat)(i, j) =
          track_object_distance_->ComputeDistance(new_objects[j], tracks[i]);
    }
  }
}

  下面我们看下激光测量object与追踪track的之间距离计算方式:关于具体每个权重计算函数可以在distance_collection.h查看。

float MlfTrackObjectDistance::ComputeDistance(
    const TrackedObjectConstPtr& object,
    const MlfTrackDataConstPtr& track) const 
{ 
  // 是否为背景目标
  bool is_background = object->is_background;
  // 获取最新的追踪目标
  const TrackedObjectConstPtr latest_object = track->GetLatestObject().second;
  std::string key = latest_object->sensor_info.name + object->sensor_info.name;
  const std::vector<float>* weights = nullptr;
  if (is_background)  // 判断是否为背景
  {
    auto iter = background_weight_table_.find(key);
    if (iter == background_weight_table_.end()) {
      weights = &kBackgroundDefaultWeight; // 背景权重超参
    } else {
      weights = &iter->second;
    }
  } 
  else {
    auto iter = foreground_weight_table_.find(key);
    if (iter == foreground_weight_table_.end()) {
      weights = &kForegroundDefaultWeight; // 前景超参
    } else {
      weights = &iter->second;
    }
  }
  if (weights == nullptr || weights->size() < 7) {
    AERROR << "Invalid weights";
    return 1e+10f;
  }
  float distance = 0.f;
  float delta = 1e-10f;

  double current_time = object->object_ptr->latest_tracked_time;
  // 根据最新的时间预测
  track->PredictState(current_time);
  // 时间差
  double time_diff =
      track->age_ ? current_time - track->latest_visible_time_ : 0;
  if (weights->at(0) > delta) {
    distance +=
        weights->at(0) * LocationDistance(latest_object, track->predict_.state, object, time_diff); // 位置距离
  }
  if (weights->at(1) > delta) {
    distance +=
        weights->at(1) * DirectionDistance(latest_object, track->predict_.state, object, time_diff); // 朝向距离
  }
  if (weights->at(2) > delta) {
    distance +=
        weights->at(2) * BboxSizeDistance(latest_object, track->predict_.state, object, time_diff); // box size距离计算
  }
  if (weights->at(3) > delta) {
    distance +=
        weights->at(3) * PointNumDistance(latest_object, track->predict_.state, object, time_diff);  // 点云点集距离计算
  }
  if (weights->at(4) > delta) {
    distance +=
        weights->at(4) * HistogramDistance(latest_object, track->predict_.state, object, time_diff); // 直方图距离计算
  }
  if (weights->at(5) > delta) {
    distance += weights->at(5) * CentroidShiftDistance(latest_object,
                                                       track->predict_.state, object, time_diff); // 重心
  }
  if (weights->at(6) > delta) {
    distance += weights->at(6) *
                BboxIouDistance(latest_object, track->predict_.state, object, time_diff, background_object_match_threshold_); // box iou计算权重
  }
  // for foreground, calculate semantic map based distance
  //  if (!is_background) {
  //    distance += weights->at(7) * SemanticMapDistance(*track, object);
  //  }
  return distance;
}

  计算出objecttrackobject之间的权重获取关联矩阵后,判断是否为背景目标,如果是使用GnnBipartiteGraphMatcher。否则使用MultiHmBipartiteGraphMatcher来求解出关联结果。

lidar-tracker代码模块之状态更新

  在上述关联结果之后,需要使用测量值object对追踪列表进行状态更新。从上述结构来看总共有两种滤波方式,MlfShapeFilterMlfMotionFilter。关于object的更新部分主要包括基础的中心点、框大小等。其中MlfMotionFilter更新状态速度使用部分卡尔曼状态监测方式。关于MlfBaseFilter为两者的对外接口类,Apollo通过一个std::vector来存储多种滤波方式,以此来进行不同变量使用不同的滤波方式。请看MlfTracker的初始化代码可以发现:

bool MlfTracker::Init(const MlfTrackerInitOptions options) {
  auto config_manager = lib::ConfigManager::Instance();
  const lib::ModelConfig* model_config = nullptr;
  ACHECK(config_manager->GetModelConfig(Name(), &model_config));
  const std::string work_root = config_manager->work_root();
  std::string config_file;
  std::string root_path;
  ACHECK(model_config->get_value("root_path", &root_path));
  config_file = GetAbsolutePath(work_root, root_path);
  config_file = GetAbsolutePath(config_file, "mlf_tracker.conf");
  MlfTrackerConfig config;
  ACHECK(cyber::common::GetProtoFromFile(config_file, &config));
  // 通过循环访问配置文件里面的filter name来获取多种的filter 
  for (int i = 0; i < config.filter_name_size(); ++i) {
    const auto& name = config.filter_name(i);
    MlfBaseFilter* filter = MlfBaseFilterRegisterer::GetInstanceByName(name);
    ACHECK(filter);
    MlfFilterInitOptions filter_init_options;
    ACHECK(filter->Init(filter_init_options));
    filters_.push_back(filter); // 这里通过filters_来存储多种的滤波方式
    AINFO << "MlfTracker add filter: " << filter->Name();
  }
  return true;
}
lidar-tracker代码模块之measurement

  在measurement测量值模块中,Apollo采用以下几种特征计算:下述方式主要是在运动估计平滑时候使用得到相关的测量值。

void MlfMotionMeasurement::ComputeMotionMeasurment(
    const MlfTrackDataConstPtr& track_data, TrackedObjectPtr new_object) {
  // prefer to choose objects from the same sensor
  std::string sensor_name = new_object->sensor_info.name;
  TrackedObjectConstPtr latest_object =
      track_data->GetLatestSensorObject(sensor_name).second;
  if (latest_object == nullptr) {
    latest_object = track_data->GetLatestObject().second;
  }
  if (latest_object.get() == nullptr) {
    AERROR << "latest_object is not available";
    return;
  }
  // should we estimate the measurement if the time diff is too small?
  double latest_time = latest_object->object_ptr->latest_tracked_time;
  double current_time = new_object->object_ptr->latest_tracked_time;
  double time_diff = current_time - latest_time;
  if (fabs(time_diff) < EPSILON_TIME) {
    time_diff = DEFAULT_FPS;
  }
  // 激光点云轮廓点集的速度测量
  MeasureAnchorPointVelocity(new_object, latest_object, time_diff);
  // 激光点云box的中心点速度测量
  MeasureBboxCenterVelocity(new_object, latest_object, time_diff);
  // 激光点云box的角点速度测量
  MeasureBboxCornerVelocity(new_object, latest_object, time_diff);
  // 测量值的选择 依据运动一致性方法
  MeasurementSelection(track_data, latest_object, new_object);
  // 测量值的质量估计
  MeasurementQualityEstimation(latest_object, new_object);
}

  Apollo激光雷达tracker部分的流程与数据结构大概就是这么多,具体到里面的算法单元细节后续在逐步介绍。本篇主要是讲解其中的数据结构与调用流程,以及相对关键的部分模块细节。

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: Apollo Planning是一个自动驾驶规划模块,它负责生成自动驾驶车辆的行驶路线和行驶轨迹。该模块代码主要包括以下几个部分: 1. 地图数据处理:该部分代码主要负责处理地图数据,包括地图的加载、解析和存储等。 2. 车辆状态估计:该部分代码主要负责估计车辆的状态,包括车辆的位置、速度、加速度等。 3. 障碍物检测:该部分代码主要负责检测车辆周围的障碍物,包括车辆前方的障碍物、车辆后方的障碍物等。 4. 路径规划:该部分代码主要负责生成车辆的行驶路线,包括起点、终点、途经点等。 5. 轨迹规划:该部分代码主要负责生成车辆的行驶轨迹,包括车辆的速度、加速度、转向角度等。 总的来说,Apollo Planning的代码解读需要对自动驾驶技术有一定的了解,需要熟悉相关的算法和数据结构。同时,还需要对C++编程语言有一定的掌握,能够理解和修改代码。 ### 回答2: Apollo Planning是Apollo平台中的一部分,是一种规划算法,用于生成具有速度、加速度、路径跟踪、动态碰撞检测等约束条件的行驶路径。本文将对Apollo Planning中的代码进行解读Apollo Planning的核心代码包括两个部分:路径规划器和速度规划器。其中路径规划器的主要任务是在路网中寻找一条从起点到终点的路径,而速度规划器的主要任务则是为规划出的路径生成相应的速度规划和轨迹。 路径规划器中采用的主要算法是基于A*算法的全局规划器和基于Dijkstra算法的局部规划器。全局规划器用于从起点到终点寻找全局路径,而局部规划器则用于在全局路径的基础上进行优化,以生成最终的路径。 在速度规划器中,采用了二次规划、线性插值和基于速度和加速度约束的时间分配等算法,用于根据路网上提供的速度信息和预计的路况等因素生成规划速度和轨迹。 除此之外,还应用了动态碰撞检测算法,用于在行驶过程中实时检测障碍物,并调整行驶路径以避免碰撞。 总之,Apollo Planning的代码实现了较为完善的路径规划和速度规划功能,并且综合应用了多种算法和约束条件,使得车辆行驶更加安全、稳定。 ### 回答3: Apollo Planning 代码是百度自动驾驶平台 Apollo 中用于路径规划的组件。通过对代码解读,我们可以了解到路径规划背后的一系列算法和原理。 首先,Apollo Planning 首先需要载入地图信息,以确定行驶的区域和道路网络。这些地图信息包括道路形状、道路宽度、车道数量、速度限制和限制规则等。 然后,Apollo Planning 根据车辆当前位置和目的地位置,通过 A*算法或 Dijkstra 算法等规划出车辆行驶的路径。这一过程中,Apollo Planning 需要考虑各种限制条件,如道路的长度、转弯半径、速度限制、停止标志和交通信号灯等。 接下来,Apollo Planning 将规划出的路径转换为轨迹,以让车辆根据轨迹规划进行动作。这一过程需要考虑车辆的动力学特性,比如加速度、最大速度限制和最大转弯速度等。 在最终生成的行驶轨迹中,需要包含一些基础信息,如轨迹的时间戳、各个点的速度和加速度信息等。这些信息有助于车辆在运行过程中准确地遵守路径规划,并在行驶中做出适时的调整。 总之,Apollo Planning 的核心功能是确定车辆行驶的路线、行驶轨迹和行驶速度等。该组件通过高效的算法和细致的条件考虑,实现自动驾驶车辆的稳定、安全和高效的路径规划。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值