欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Apollo perception源码阅读 | radar

程序员文章站 2022-07-12 11:56:55
...

#! https://zhuanlan.zhihu.com/p/391021814

Apollo perception源码阅读 | radar

本文为Apollo感知融合源码阅读笔记,建议参照Apollo6.0源码阅读本文,水平有限,有错误的地方希望大佬多加指正!
个人代码注释链接: leox24 / perception_learn

Radar模块

主要是radar检测的框架代码,其实并没有什么核心内容…
具体也就是融合的那一套东西,预处理,关联匹配,跟踪,虽然radar有写滤波器adaptive kalman,但是实际上并没有用。

1 目录结构

.
├── app
│   ├── BUILD
│   ├── radar_obstacle_perception.cc //框架实现
│   └── radar_obstacle_perception.h
├── common
│   ├── BUILD
│   ├── radar_util.cc
│   ├── radar_util.h
│   └── types.h
└── lib
    ├── detector
    ├── dummy
    ├── interface
    ├── preprocessor
    ├── roi_filter
    └── tracker

2 入口

Apollo/modules/perception/production/launch并没有单独的启动radar的launch文件,也没有单一的dag文件,雷达组件的定义都在融合的dag文件中Apollo/modules/perception/production/dag/dag_streaming_perception.dag

  components {
    class_name: "RadarDetectionComponent"
    config {
      name: "FrontRadarDetection"
      config_file_path: "/apollo/modules/perception/production/conf/perception/radar/front_radar_component_conf.pb.txt"
      readers {
          channel: "/apollo/sensor/radar/front"
        }
    }
  }

  components {
    class_name: "RadarDetectionComponent"
    config {
      name: "RearRadarDetection"
      config_file_path: "/apollo/modules/perception/production/conf/perception/radar/rear_radar_component_conf.pb.txt"
      readers {
          channel: "/apollo/sensor/radar/rear"
        }
    }
  }

可见radar的组件类是RadarDetectionComponent,分别对前雷达和后雷达进行处理。

2.1 RadarDetectionComponent

Apollo/modules/perception/onboard/component/radar_detection_component.cc

  • RadarDetectionComponent::Init()
    初始化参数,参数列表如下:
  radar_name: "radar_front"
  tf_child_frame_id: "radar_front"
  radar_forward_distance: 200.0
  radar_preprocessor_method: "ContiArsPreprocessor"
  radar_perception_method: "RadarObstaclePerception"
  radar_pipeline_name: "FrontRadarPipeline"
  odometry_channel_name: "/apollo/localization/pose"
  output_channel_name: "/perception/inner/PrefusedObjects"

  radar_name: "radar_rear"
  tf_child_frame_id: "radar_rear"
  radar_forward_distance: 120.0
  radar_preprocessor_method: "ContiArsPreprocessor"
  radar_perception_method: "RadarObstaclePerception"
  radar_pipeline_name: "RearRadarPipeline"
  odometry_channel_name: "/apollo/localization/pose"
  output_channel_name: "/perception/inner/PrefusedObjects"

  FLAGS_obs_enable_hdmap_input=true
  • RadarDetectionComponent::InternalProc()
    • 输入:ContiRadar原始radar信息
    • 输出:SensorFrameMessageradar处理结果
      主要是两步,Preprocess,Perceive,下文展开介绍预处理和核心处理函数。
bool RadarDetectionComponent::InternalProc(
    const std::shared_ptr<ContiRadar>& in_message,
    std::shared_ptr<SensorFrameMessage> out_message) {

  // 初始化预处理参数
  // Init preprocessor_options
  radar::PreprocessorOptions preprocessor_options;
  // 预处理结果
  ContiRadar corrected_obstacles;
  // 预处理
  radar_preprocessor_->Preprocess(raw_obstacles, preprocessor_options,
                                  &corrected_obstacles);
  PERF_BLOCK_END_WITH_INDICATOR(radar_info_.name, "radar_preprocessor");
  timestamp = corrected_obstacles.header().timestamp_sec();

  out_message->timestamp_ = timestamp;
  out_message->seq_num_ = seq_num_;
  out_message->process_stage_ = ProcessStage::LONG_RANGE_RADAR_DETECTION;
  out_message->sensor_id_ = radar_info_.name;

  // 初始化参数:radar2world转换矩阵,radar2自车转换矩阵、自车线速度和角速度
  // Init radar perception options
  radar::RadarPerceptionOptions options;
  options.sensor_name = radar_info_.name;
  // Init detector_options
  Eigen::Affine3d radar_trans;
  // radar2world的转换矩阵
  if (!radar2world_trans_.GetSensor2worldTrans(timestamp, &radar_trans)) {
    out_message->error_code_ = apollo::common::ErrorCode::PERCEPTION_ERROR_TF;
    AERROR << "Failed to get pose at time: " << timestamp;
    return true;
  }
  Eigen::Affine3d radar2novatel_trans;
  // radar2自车的转换矩阵
  if (!radar2novatel_trans_.GetTrans(timestamp, &radar2novatel_trans, "novatel",
                                     tf_child_frame_id_)) {
    out_message->error_code_ = apollo::common::ErrorCode::PERCEPTION_ERROR_TF;
    AERROR << "Failed to get radar2novatel trans at time: " << timestamp;
    return true;
  }
  PERF_BLOCK_END_WITH_INDICATOR(radar_info_.name, "GetSensor2worldTrans");
  Eigen::Matrix4d radar2world_pose = radar_trans.matrix();
  options.detector_options.radar2world_pose = &radar2world_pose;
  Eigen::Matrix4d radar2novatel_trans_m = radar2novatel_trans.matrix();
  options.detector_options.radar2novatel_trans = &radar2novatel_trans_m;
  // 自车车速
  if (!GetCarLocalizationSpeed(timestamp,
                               &(options.detector_options.car_linear_speed),
                               &(options.detector_options.car_angular_speed))) {
    AERROR << "Failed to call get_car_speed. [timestamp: " << timestamp;
    // return false;
  }
  PERF_BLOCK_END_WITH_INDICATOR(radar_info_.name, "GetCarSpeed");
  // Init roi_filter_options
  // radar到world的T矩阵偏移量
  base::PointD position;
  position.x = radar_trans(0, 3);
  position.y = radar_trans(1, 3);
  position.z = radar_trans(2, 3);
  options.roi_filter_options.roi.reset(new base::HdmapStruct());
  if (FLAGS_obs_enable_hdmap_input) {
    hdmap_input_->GetRoiHDMapStruct(position, radar_forward_distance_,
                                    options.roi_filter_options.roi);
  }
  PERF_BLOCK_END_WITH_INDICATOR(radar_info_.name, "GetRoiHDMapStruct");
  // Init object_filter_options
  // Init track_options
  // Init object_builder_options
  // 处理预处理后的radar obj
  std::vector<base::ObjectPtr> radar_objects;
  if (!radar_perception_->Perceive(corrected_obstacles, options,
                                   &radar_objects)) {
    out_message->error_code_ =
        apollo::common::ErrorCode::PERCEPTION_ERROR_PROCESS;
    AERROR << "RadarDetector Proc failed.";
    return true;
  }
  out_message->frame_.reset(new base::Frame());
  out_message->frame_->sensor_info = radar_info_;
  out_message->frame_->timestamp = timestamp;
  out_message->frame_->sensor2world_pose = radar_trans;
  out_message->frame_->objects = radar_objects;

  return true;
}

3 ContiArsPreprocessor

Apollo/modules/perception/radar/lib/preprocessor/conti_ars_preprocessor/conti_ars_preprocessor.cc

  • ContiArsPreprocessor::Preprocess()
    雷达预处理的实现,包括跳过不在时间范围内的目标,重新分配ID,修正时间戳(减去0.07)三个步骤。个人觉得这个预处理是跟传感器数据的特征有关的,不同传感器有不同的预处理方法(经验),对我来说并没有什么参考价值。感兴趣的可以看我的注释代码,我就不展开阐述了。
// 雷达预处理
bool ContiArsPreprocessor::Preprocess(
    const drivers::ContiRadar& raw_obstacles,
    const PreprocessorOptions& options,
    drivers::ContiRadar* corrected_obstacles) {
  PERF_FUNCTION();
  SkipObjects(raw_obstacles, corrected_obstacles);
  ExpandIds(corrected_obstacles);
  CorrectTime(corrected_obstacles);
  return true;
}

4 RadarObstaclePerception

Apollo/modules/perception/radar/app/radar_obstacle_perception.cc

  • RadarObstaclePerception::Perceive()
    核心处理函数,包括检测,ROI过滤,跟踪三个步骤。
bool RadarObstaclePerception::Perceive(
    const drivers::ContiRadar& corrected_obstacles,
    const RadarPerceptionOptions& options,
    std::vector<base::ObjectPtr>* objects) {
  base::FramePtr detect_frame_ptr(new base::Frame());
  // 检测 ContiArsDetector
  if (!detector_->Detect(corrected_obstacles, options.detector_options,
                         detect_frame_ptr)) {
    AERROR << "radar detect error";
    return false;
  }
  // ROI过滤 HdmapRadarRoiFilter
  if (!roi_filter_->RoiFilter(options.roi_filter_options, detect_frame_ptr)) {
    ADEBUG << "All radar objects were filtered out";
  }
  base::FramePtr tracker_frame_ptr(new base::Frame);

  // 跟踪 ContiArsTracker
  if (!tracker_->Track(*detect_frame_ptr, options.track_options,
                       tracker_frame_ptr)) {
    AERROR << "radar track error";
    return false;
  }
  *objects = tracker_frame_ptr->objects;

  return true;
}

4.1 ContiArsDetector::Detect

Apollo/modules/perception/radar/lib/detector/conti_ars_detector/conti_ars_detector.cc
说是检测,其实是对传感器数据的处理,radar2worldradar2自车的坐标系转换,相对速度和绝对速度的转换,运动状态、类别等属性的赋值,这部分对做radar的是可以有一定参考的。
其中需要注意的是转换速度时,需要补偿自车转弯旋转导致的速度变化,就是在radar速度的xy分量上补偿掉自车角速度带来的速度。

void ContiArsDetector::RawObs2Frame(
    const drivers::ContiRadar& corrected_obstacles,
    const DetectorOptions& options, base::FramePtr radar_frame) {
  // radar2world转换矩阵
  const Eigen::Matrix4d& radar2world = *(options.radar2world_pose);
  // radar到自车转换矩阵
  const Eigen::Matrix4d& radar2novatel = *(options.radar2novatel_trans);
  // 自车角速度,应该是xyz三个方向上的角速度,应该只有转弯时的yawrate
  const Eigen::Vector3f& angular_speed = options.car_angular_speed;

  Eigen::Matrix3d rotation_novatel;
  rotation_novatel << 0, -angular_speed(2), angular_speed(1), angular_speed(2),
      0, -angular_speed(0), -angular_speed(1), angular_speed(0), 0;
  // 补偿自车转弯旋转时的速度变化。
  Eigen::Matrix3d rotation_radar = radar2novatel.topLeftCorner(3, 3).inverse() *
                                   rotation_novatel *
                                   radar2novatel.topLeftCorner(3, 3);
  Eigen::Matrix3d radar2world_rotate = radar2world.block<3, 3>(0, 0);
  Eigen::Matrix3d radar2world_rotate_t = radar2world_rotate.transpose();
  // Eigen::Vector3d radar2world_translation = radar2world.block<3, 1>(0, 3);
  ADEBUG << "radar2novatel: " << radar2novatel;
  ADEBUG << "angular_speed: " << angular_speed;
  ADEBUG << "rotation_radar: " << rotation_radar;
  for (const auto radar_obs : corrected_obstacles.contiobs()) {
    base::ObjectPtr radar_object(new base::Object);
    radar_object->id = radar_obs.obstacle_id();
    radar_object->track_id = radar_obs.obstacle_id();
    // 局部位姿
    Eigen::Vector4d local_loc(radar_obs.longitude_dist(),
                              radar_obs.lateral_dist(), 0, 1);
    // 世界位姿
    Eigen::Vector4d world_loc =
        static_cast<Eigen::Matrix<double, 4, 1, 0, 4, 1>>(radar2world *
                                                          local_loc);
    // 世界坐标系下的xy,z=0
    radar_object->center = world_loc.block<3, 1>(0, 0);
    radar_object->anchor_point = radar_object->center;

    // 相对速度
    Eigen::Vector3d local_vel(radar_obs.longitude_vel(),
                              radar_obs.lateral_vel(), 0);
    // 补偿自车转弯旋转时的速度变化。雷达的相对速度的xy分量,加减自车转弯的xy速度分量
    Eigen::Vector3d angular_trans_speed =
        rotation_radar * local_loc.topLeftCorner(3, 1);
    Eigen::Vector3d world_vel =
        static_cast<Eigen::Matrix<double, 3, 1, 0, 3, 1>>(
            radar2world_rotate * (local_vel + angular_trans_speed));
    // 绝对速度
    Eigen::Vector3d vel_temp =
        world_vel + options.car_linear_speed.cast<double>();
    radar_object->velocity = vel_temp.cast<float>();

    Eigen::Matrix3d dist_rms;
    dist_rms.setZero();
    Eigen::Matrix3d vel_rms;
    vel_rms.setZero();
    // rms是均方根,但这里可能是指方差相关?
    dist_rms(0, 0) = radar_obs.longitude_dist_rms();
    dist_rms(1, 1) = radar_obs.lateral_dist_rms();
    vel_rms(0, 0) = radar_obs.longitude_vel_rms();
    vel_rms(1, 1) = radar_obs.lateral_vel_rms();
    // 计算位置不确定性
    radar_object->center_uncertainty =
        (radar2world_rotate * dist_rms * dist_rms.transpose() *
         radar2world_rotate_t)
            .cast<float>();
    // 计算速度不确定性
    radar_object->velocity_uncertainty =
        (radar2world_rotate * vel_rms * vel_rms.transpose() *
         radar2world_rotate_t)
            .cast<float>();
    double local_obj_theta = radar_obs.oritation_angle() / 180.0 * PI;
    Eigen::Vector3f direction(static_cast<float>(cos(local_obj_theta)),
                              static_cast<float>(sin(local_obj_theta)), 0.0f);
    // 方向和角度
    direction = radar2world_rotate.cast<float>() * direction;
    radar_object->direction = direction;
    radar_object->theta = std::atan2(direction(1), direction(0));
    radar_object->theta_variance =
        static_cast<float>(radar_obs.oritation_angle_rms() / 180.0 * PI);
    radar_object->confidence = static_cast<float>(radar_obs.probexist());
    
    // 运动状态:运动、静止、未知
    // 目标置信度
    int motion_state = radar_obs.dynprop();
    double prob_target = radar_obs.probexist();
    if ((prob_target > MIN_PROBEXIST) &&
        (motion_state == CONTI_MOVING || motion_state == CONTI_ONCOMING ||
         motion_state == CONTI_CROSSING_MOVING)) {
      radar_object->motion_state = base::MotionState::MOVING;
    } else if (motion_state == CONTI_DYNAMIC_UNKNOWN) {
      radar_object->motion_state = base::MotionState::UNKNOWN;
    } else {
      radar_object->motion_state = base::MotionState::STATIONARY;
      radar_object->velocity.setZero();
    }
    // 类别
    int cls = radar_obs.obstacle_class();
    if (cls == CONTI_CAR || cls == CONTI_TRUCK) {
      radar_object->type = base::ObjectType::VEHICLE;
    } else if (cls == CONTI_PEDESTRIAN) {
      radar_object->type = base::ObjectType::PEDESTRIAN;
    } else if (cls == CONTI_MOTOCYCLE || cls == CONTI_BICYCLE) {
      radar_object->type = base::ObjectType::BICYCLE;
    } else {
      radar_object->type = base::ObjectType::UNKNOWN;
    }
    // 长宽高
    radar_object->size(0) = static_cast<float>(radar_obs.length());
    radar_object->size(1) = static_cast<float>(radar_obs.width());
    radar_object->size(2) = 2.0f;  // vehicle template (pnc required)
    if (cls == CONTI_POINT) {
      radar_object->size(0) = 1.0f;
      radar_object->size(1) = 1.0f;
    }
    // extreme case protection
    if (radar_object->size(0) * radar_object->size(1) < 1.0e-4) {
      if (cls == CONTI_CAR || cls == CONTI_TRUCK) {
        radar_object->size(0) = 4.0f;
        radar_object->size(1) = 1.6f;  // vehicle template
      } else {
        radar_object->size(0) = 1.0f;
        radar_object->size(1) = 1.0f;
      }
    }
    MockRadarPolygon(radar_object);

    float local_range = static_cast<float>(local_loc.head(2).norm());
    float local_angle =
        static_cast<float>(std::atan2(local_loc(1), local_loc(0)));
    radar_object->radar_supplement.range = local_range;
    radar_object->radar_supplement.angle = local_angle;

    radar_frame->objects.push_back(radar_object);

  }
}

4.2 HdmapRadarRoiFilter::RoiFilter

Apollo/modules/perception/radar/lib/roi_filter/hdmap_radar_roi_filter/hdmap_radar_roi_filter.cc
和lidar的处理一样,过滤掉ROI区域的目标。

bool HdmapRadarRoiFilter::RoiFilter(const RoiFilterOptions& options,
                                    base::FramePtr radar_frame) {
  // 滤除Roi范围外的障碍物
  std::vector<base::ObjectPtr> origin_objects = radar_frame->objects;
  return common::ObjectInRoiCheck(options.roi, origin_objects,
                                  &radar_frame->objects);
}

4.3 ContiArsTracker::Track

Apollo/modules/perception/radar/lib/tracker/conti_ars_tracker/conti_ars_tracker.cc
跟踪也就是经典的关联,匹配,更新。
关联值只使用了距离计算,匹配使用了ID匹配和匈牙利匹配,更新则是直接更新,没有使用滤波

  • ContiArsTracker::TrackObjects()
void ContiArsTracker::TrackObjects(const base::Frame &radar_frame) {
  std::vector<TrackObjectPair> assignments;
  std::vector<size_t> unassigned_tracks;
  std::vector<size_t> unassigned_objects;
  TrackObjectMatcherOptions matcher_options;
  const auto &radar_tracks = track_manager_->GetTracks();
  // 关联匹配
  matcher_->Match(radar_tracks, radar_frame, matcher_options, &assignments,
                  &unassigned_tracks, &unassigned_objects);
  // 更新四步骤:更新匹配的,更新未匹配的,删除丢失的,创建新的
  UpdateAssignedTracks(radar_frame, assignments);
  // 超过0.06秒设置为死亡
  UpdateUnassignedTracks(radar_frame, unassigned_tracks);
  DeleteLostTracks();
  CreateNewTracks(radar_frame, unassigned_objects);
}

  • HMMatcher::Match()
    这里要注意的是HMMatcherBaseMatcher的派生类,IDMatch()是基类的实现,而在IDMatch()中有个RefinedTrack()是派生类重写了的,会调用派生类的RefinedTrack()
bool HMMatcher::Match(const std::vector<RadarTrackPtr> &radar_tracks,
                      const base::Frame &radar_frame,
                      const TrackObjectMatcherOptions &options,
                      std::vector<TrackObjectPair> *assignments,
                      std::vector<size_t> *unassigned_tracks,
                      std::vector<size_t> *unassigned_objects) {
  // ID匹配,相同id且距离小于2.5直接匹配上
  IDMatch(radar_tracks, radar_frame, assignments, unassigned_tracks,
          unassigned_objects);
  // 计算关联值,匈牙利匹配
  TrackObjectPropertyMatch(radar_tracks, radar_frame, assignments,
                           unassigned_tracks, unassigned_objects);
  return true;
}
  • HMMatcher::TrackObjectPropertyMatch()
    这里几乎和fusion的匹配一样,都是计算关联值之后使用匈牙利进行匹配,唯一的区别是关联值的计算方式。
void HMMatcher::TrackObjectPropertyMatch(
    const std::vector<RadarTrackPtr> &radar_tracks,
    const base::Frame &radar_frame, std::vector<TrackObjectPair> *assignments,
    std::vector<size_t> *unassigned_tracks,
    std::vector<size_t> *unassigned_objects) {
  if (unassigned_tracks->empty() || unassigned_objects->empty()) {
    return;
  }
  std::vector<std::vector<double>> association_mat(unassigned_tracks->size());
  for (size_t i = 0; i < association_mat.size(); ++i) {
    association_mat[i].resize(unassigned_objects->size(), 0);
  }
  // 计算关联矩阵
  ComputeAssociationMat(radar_tracks, radar_frame, *unassigned_tracks,
                        *unassigned_objects, &association_mat);

  // from perception-common
  common::SecureMat<double> *global_costs =
      hungarian_matcher_.mutable_global_costs();
  global_costs->Resize(unassigned_tracks->size(), unassigned_objects->size());
  for (size_t i = 0; i < unassigned_tracks->size(); ++i) {
    for (size_t j = 0; j < unassigned_objects->size(); ++j) {
      (*global_costs)(i, j) = association_mat[i][j];
    }
  }
  std::vector<TrackObjectPair> property_assignments;
  std::vector<size_t> property_unassigned_tracks;
  std::vector<size_t> property_unassigned_objects;
  // 匈牙利匹配,和fusion一样
  hungarian_matcher_.Match(
      BaseMatcher::GetMaxMatchDistance(), BaseMatcher::GetBoundMatchDistance(),
      common::GatedHungarianMatcher<double>::OptimizeFlag::OPTMIN,
      &property_assignments, &property_unassigned_tracks,
      &property_unassigned_objects);
...(省略)
}

radar这里的关联值的距离计算了两次,然后取了平均。分别用了上一帧和当前帧的速度做预测。

// 计算航迹往前预测和观测的中心点距离
double distance_forward = DistanceBetweenObs(
    track_object, track_timestamp, frame_object, frame_timestamp);
// 计算观测往后预测和航迹的中心点距离
double distance_backward = DistanceBetweenObs(
    frame_object, frame_timestamp, track_object, track_timestamp);
association_mat->at(i).at(j) =
    0.5 * distance_forward + 0.5 * distance_backward;

  • ContiArsTracker::UpdateAssignedTracks()
    这里的更新track,虽然写了adaptive kalman,但是并没有使用滤波器,而是直接用观测量做了更新,猜测可能是传感器供应商已经做好了跟踪。简单看了下adaptive kalman,好像也就是CV模型的KF。
    剩下的更新未匹配的函数,删除丢失track,创建新track也没有什么需要阐述的,就是简单的处理。

5 小结

radar模块的处理并没有很多,所以也没有很详细的写,有兴趣可以自己看看,大多是比较简单的实现。
欢迎指正~