目录
1. 模块概述
1.1 功能定位
定位模块是Apollo自动驾驶系统的核心模块之一,负责提供车辆的精确位置、姿态和速度信息。该模块支持三种定位方法:
- RTK定位:融合GPS和IMU数据
- MSF多传感器融合定位:融合GPS、IMU和激光雷达数据
- NDT定位:基于正态分布变换的激光雷达定位
1.2 目录结构
modules/localization/
├── BUILD                    # Bazel构建文件
├── common/                  # 通用工具和数据结构
│   ├── gnss_compensator.cc  # GNSS时间戳补偿器
│   └── localization_gflags.cc # gflags定义
├── conf/                    # 配置文件
│   ├── localization.conf    # 主配置文件(gflags)
│   └── rtk_localization.pb.txt # RTK配置
├── dag/                     # CyberRT DAG配置
│   ├── dag_streaming_rtk_localization.dag
│   ├── dag_streaming_msf_localization.dag
│   └── dag_streaming_ndt_localization.dag
├── launch/                  # 启动文件
│   ├── rtk_localization.launch
│   ├── msf_localization.launch
│   └── ndt_localization.launch
├── msf/                     # 多传感器融合定位实现
│   ├── local_integ/         # 融合算法核心
│   ├── local_map/           # 地图相关
│   ├── local_pyramid_map/   # 金字塔地图
│   └── local_tool/          # 工具集
├── ndt/                     # NDT定位实现
│   ├── ndt_locator/         # NDT定位器
│   └── map_creation/        # 地图创建
├── rtk/                     # RTK定位实现
├── proto/                   # Protobuf协议定义
└── testdata/                # 测试数据
1.3 输入输出
输入:
- GPS消息:/apollo/sensor/gnss/odometry
- IMU消息:/apollo/sensor/gnss/imu
- 点云消息:/apollo/sensor/hesai40/compensator/PointCloud2
- GNSS状态:/apollo/sensor/gnss/ins_stat
输出:
- 定位结果:/apollo/localization/pose(LocalizationEstimate)
- 定位状态:/apollo/localization/msf_status(LocalizationStatus)
2. 架构设计
2.1 整体架构
定位模块基于CyberRT框架,采用组件化设计。每种定位方法都实现为独立的Component:
┌─────────────────────────────────────────────────────────┐
│                   Localization Module                    │
├─────────────────────────────────────────────────────────┤
│                                                           │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │
│  │ RTKLocalization │ MSFLocalization │ NDTLocalization │ │
│  │   Component     │   Component     │   Component     │ │
│  └──────┬─────────┘  └──────┬─────────┘  └──────┬──────┘ │
│         │                   │                   │         │
│         ▼                   ▼                   ▼         │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │
│  │RTKLocalization│  │MSFLocalization│  │NDTLocalization│  │
│  │     (Core)    │  │     (Core)    │  │     (Core)    │  │
│  └───────────────┘  └───────┬──────┘  └───────┬────────┘ │
│                              │                  │          │
│                      ┌───────┴────────┐  ┌──────┴───────┐ │
│                      │LocalizationInteg│  │LidarLocatorNDT│ │
│                      └─────────────────┘  └──────────────┘ │
└─────────────────────────────────────────────────────────┘
2.2 Component层设计
RTKLocalizationComponent
文件位置:rtk/rtk_localization_component.h
class RTKLocalizationComponent final
    : public cyber::Component<localization::Gps> {
 public:
  bool Init() override;
  bool Proc(const std::shared_ptr<localization::Gps> &gps_msg) override;
 private:
  // 订阅者
  std::shared_ptr<cyber::Reader<localization::CorrectedImu>>
      corrected_imu_listener_;
  std::shared_ptr<cyber::Reader<drivers::gnss::InsStat>>
      gps_status_listener_;
  // 发布者
  std::shared_ptr<cyber::Writer<LocalizationEstimate>>
      localization_talker_;
  std::shared_ptr<cyber::Writer<LocalizationStatus>>
      localization_status_talker_;
  // 核心定位实例
  std::unique_ptr<RTKLocalization> localization_;
  // TF广播器
  std::unique_ptr<apollo::transform::TransformBroadcaster> tf2_broadcaster_;
};
关键特性:
- 订阅GPS消息作为主触发(Proc函数)
- 异步订阅IMU和GPS状态
- 支持IMU外参补偿
- 发布TF变换和定位结果
MSFLocalizationComponent
文件位置:msf/msf_localization_component.h
class MSFLocalizationComponent final
    : public cyber::Component<drivers::gnss::Imu> {
 public:
  bool Init() override;
  bool Proc(const std::shared_ptr<drivers::gnss::Imu>& imu_msg) override;
 private:
  // 订阅者
  std::shared_ptr<cyber::Reader<drivers::PointCloud>> lidar_listener_;
  std::shared_ptr<cyber::Reader<drivers::gnss::GnssBestPose>>
      bestgnsspos_listener_;
  std::shared_ptr<cyber::Reader<drivers::gnss::Heading>>
      gnss_heading_listener_;
  // 核心定位实例
  MSFLocalization localization_;
  // 消息发布器
  std::shared_ptr<LocalizationMsgPublisher> publisher_;
};
关键特性:
- 订阅IMU作为主触发(高频驱动)
- 异步订阅激光雷达、GNSS BestPose和航向信息
- 多传感器时间同步
- 分离的发布器设计
NDTLocalizationComponent
文件位置:ndt/ndt_localization_component.h
class NDTLocalizationComponent final
    : public cyber::Component<localization::Gps> {
 public:
  bool Init() override;
  bool Proc(const std::shared_ptr<localization::Gps> &odometry_msg) override;
 private:
  void LidarCallback(const std::shared_ptr<drivers::PointCloud> &lidar_msg);
  void OdometryStatusCallback(
      const std::shared_ptr<drivers::gnss::InsStat> &status_msg);
  std::shared_ptr<cyber::Reader<drivers::PointCloud>> lidar_listener_;
  std::shared_ptr<cyber::Reader<drivers::gnss::InsStat>>
      odometry_status_listener_;
  std::unique_ptr<NDTLocalization> localization_;
  std::unique_ptr<apollo::transform::TransformBroadcaster> tf2_broadcaster_;
};
关键特性:
- 订阅GPS里程计作为主触发
- 异步订阅激光雷达点云
- 基于NDT算法的地图匹配
- 支持多分辨率地图
3. 三种定位方法详解
3.1 RTK定位
3.1.1 算法原理
RTK(Real-Time Kinematic)定位通过融合GPS和IMU数据提供高精度定位:
- GPS数据:提供绝对位置参考
- IMU数据:提供高频姿态和加速度信息
- 时间同步:通过插值实现GPS和IMU的时间对齐
3.1.2 核心实现
class RTKLocalization {
 public:
  void GpsCallback(const std::shared_ptr<localization::Gps> &gps_msg);
  void ImuCallback(const std::shared_ptr<localization::CorrectedImu> &imu_msg);
  void GpsStatusCallback(
      const std::shared_ptr<drivers::gnss::InsStat> &status_msg);
  void GetLocalization(LocalizationEstimate *localization);
  void GetLocalizationStatus(LocalizationStatus *localization_status);
 private:
  // IMU消息缓存队列
  std::list<localization::CorrectedImu> imu_list_;
  std::mutex imu_list_mutex_;
  // GPS状态缓存队列
  std::list<drivers::gnss::InsStat> gps_status_list_;
  std::mutex gps_status_list_mutex_;
  // IMU插值
  bool FindMatchingIMU(const double gps_timestamp_sec, CorrectedImu *imu_msg);
  bool InterpolateIMU(const CorrectedImu &imu1, const CorrectedImu &imu2,
                      const double timestamp_sec, CorrectedImu *imu_msg);
  // 数据融合
  void ComposeLocalizationMsg(const localization::Gps &gps,
                              const localization::CorrectedImu &imu,
                              LocalizationEstimate *localization);
};
3.1.3 数据流程
GPS消息到达
    ↓
查找匹配的IMU数据(时间戳对齐)
    ↓
IMU插值(如果需要)
    ↓
组合GPS位置 + IMU姿态
    ↓
应用地图偏移
    ↓
发布定位结果
3.1.4 关键技术
IMU插值算法:
template <class T>
T InterpolateXYZ(const T &p1, const T &p2, const double frac1) {
  T p;
  p.set_x((1.0 - frac1) * p1.x() + frac1 * p2.x());
  p.set_y((1.0 - frac1) * p1.y() + frac1 * p2.y());
  p.set_z((1.0 - frac1) * p1.z() + frac1 * p2.z());
  return p;
}
时间同步策略:
- 维护IMU消息缓冲队列(默认50个)
- GPS时间戳容差:1.0秒
- GPS-IMU时间差阈值:0.02秒
并发控制:
- IMU列表使用互斥锁保护
- GPS状态列表使用互斥锁保护
- 无锁化设计(读写分离)
3.2 MSF多传感器融合定位
3.2.1 算法原理
MSF(Multi-Sensor Fusion)定位是Apollo最精确的定位方法,融合:
- GNSS:提供全局位置参考
- IMU:提供高频姿态和运动信息
- LiDAR:通过地图匹配提供局部精确定位
3.2.2 核心架构
class MSFLocalization {
 public:
  // 初始化
  apollo::common::Status Init();
  void InitParams();
  // 传感器回调
  void OnPointCloud(const std::shared_ptr<drivers::PointCloud> &message);
  void OnRawImu(const std::shared_ptr<drivers::gnss::Imu> &imu_msg);
  void OnGnssBestPose(
      const std::shared_ptr<drivers::gnss::GnssBestPose> &bestgnsspos_msg);
  void OnGnssHeading(
      const std::shared_ptr<drivers::gnss::Heading> &gnss_heading_msg);
  // 定位定时器
  void OnLocalizationTimer();
 private:
  // 核心融合算法
  msf::LocalizationInteg localization_integ_;
  msf::LocalizationIntegParam localization_param_;
  msf::LocalizationMeasureState localization_state_;
  // IMU-车辆外参
  Eigen::Quaternion<double> imu_vehicle_quat_;
  Eigen::Vector3d imu_vehicle_translation_;
  // 消息发布器
  std::shared_ptr<LocalizationMsgPublisher> publisher_;
  // 定时器(用于定期发布)
  std::unique_ptr<cyber::Timer> localization_timer_;
};
3.2.3 LocalizationInteg - 融合核心
文件位置:msf/local_integ/localization_integ.h
class LocalizationInteg {
 public:
  common::Status Init(const LocalizationIntegParam ¶ms);
  // 各传感器数据处理
  void PcdProcess(const drivers::PointCloud &message);
  void RawImuProcessFlu(const drivers::gnss::Imu &imu_msg);
  void RawImuProcessRfu(const drivers::gnss::Imu &imu_msg);
  void GnssBestPoseProcess(const drivers::gnss::GnssBestPose &bestgnsspos_msg);
  void GnssHeadingProcess(const drivers::gnss::Heading &gnss_heading_msg);
  // 获取各传感器定位结果
  const LocalizationResult &GetLastestLidarLocalization() const;
  const LocalizationResult &GetLastestIntegLocalization() const;
  const LocalizationResult &GetLastestGnssLocalization() const;
 private:
  LocalizationIntegImpl *localization_integ_impl_;
};
3.2.4 数据流程
                IMU数据(高频)
                     ↓
            ┌────────┴────────┐
            │  IMU预积分      │
            │  姿态估计       │
            └────────┬────────┘
                     │
        ┌────────────┼────────────┐
        │            │            │
   GNSS数据      LiDAR点云    定时器触发
        │            │            │
        ↓            ↓            ↓
   ┌─────────┐  ┌─────────┐  ┌─────────┐
   │GNSS处理 │  │地图匹配 │  │状态融合 │
   │初始化   │  │NDT对齐  │  │EKF更新  │
   └────┬────┘  └────┬────┘  └────┬────┘
        │            │            │
        └────────────┼────────────┘
                     ↓
            ┌────────────────┐
            │ 融合定位结果   │
            │ (位置+姿态+    │
            │  速度+协方差)  │
            └────────────────┘
3.2.5 关键子模块
1. LocalizationIntegProcess:融合处理核心
- 状态预测(基于IMU)
- 观测更新(GNSS + LiDAR)
- 扩展卡尔曼滤波(EKF)
2. LocalizationLidarProcess:激光雷达处理
- 点云预处理
- NDT地图匹配
- 位姿优化
3. LocalizationGnssProcess:GNSS处理
- RTK数据解析
- 初始化检测
- 位置观测生成
4. MeasureRepublishProcess:测量重发布
- 传感器数据缓存
- 时间同步
- 数据重播
3.2.6 地图结构
MSF支持多种地图类型:
1. LosslessMap(无损地图)
- 完整保留点云数据
- 用于高精度匹配
- 文件较大
2. LossyMap(有损地图)
- 压缩点云数据
- 降低内存占用
- 适合大规模场景
3. NDTMap(正态分布地图)
- 基于正态分布建模
- 快速匹配算法
- 多分辨率支持
4. PyramidMap(金字塔地图)
- 多层级结构
- 粗到细匹配
- 提升收敛速度
3.2.7 并发设计
// IMU消息缓存(多线程安全)
std::shared_ptr<drivers::gnss::Imu> raw_imu_msg_;
std::mutex mutex_imu_msg_;
// 定时器独立线程
localization_timer_ = node_->CreateTimer(
    std::chrono::milliseconds(10),
    [this]() { OnLocalizationTimer(); }
);
并发特性:
- IMU回调独立线程
- LiDAR处理独立线程
- 定时发布独立线程
- 互斥锁保护共享数据
3.3 NDT定位
3.3.1 算法原理
NDT(Normal Distributions Transform)定位基于:
- 预先构建的NDT地图
- 实时点云数据
- GPS里程计提供初始位姿
通过NDT算法将实时点云与地图匹配,获得精确位姿。
3.3.2 核心实现
class NDTLocalization {
 public:
  void Init();
  // 传感器回调
  void OdometryCallback(const std::shared_ptr<localization::Gps>& odometry_msg);
  void LidarCallback(const std::shared_ptr<drivers::PointCloud>& lidar_msg);
  void OdometryStatusCallback(
      const std::shared_ptr<drivers::gnss::InsStat>& status_msg);
  // 获取定位结果
  void GetLocalization(LocalizationEstimate* localization) const;
  void GetLidarLocalization(LocalizationEstimate* lidar_localization) const;
  void GetLocalizationStatus(LocalizationStatus* localization_status) const;
 private:
  // 位姿缓冲区
  LocalizationPoseBuffer pose_buffer_;
  // TF查询
  transform::Buffer* tf_buffer_;
  // NDT定位器
  LidarLocatorNdt lidar_locator_;
  // 里程计缓冲区(多线程安全)
  std::list<TimeStampPose, Eigen::aligned_allocator<TimeStampPose>>
      odometry_buffer_;
  std::mutex odometry_buffer_mutex_;
  // 状态列表
  std::list<drivers::gnss::InsStat> odometry_status_list_;
  std::mutex odometry_status_list_mutex_;
  // NDT匹配质量
  double ndt_score_;
  unsigned int bad_score_count_;
};
3.3.3 数据流程
GPS里程计到达
    ↓
存入odometry_buffer_(互斥锁保护)
    ↓
LiDAR点云到达
    ↓
从TF或buffer查询预测位姿
    ↓
点云转换到地图坐标系
    ↓
NDT地图匹配
    ↓
位姿优化
    ↓
检查匹配质量(ndt_score)
    ↓
发布定位结果
3.3.4 关键技术
1. 位姿预测:
bool QueryPoseFromTF(double time, Eigen::Affine3d* pose);
bool QueryPoseFromBuffer(double time, Eigen::Affine3d* pose);
2. NDT匹配质量评估:
// 匹配分数阈值
double warnning_ndt_score_ = 1.0;   // 警告阈值
double error_ndt_score_ = 2.0;      // 错误阈值
unsigned int bad_score_count_threshold_ = 10;  // 连续失败阈值
3. 多分辨率策略:
- 支持多个分辨率等级
- 从粗到细匹配
- 提升鲁棒性
4. 并发设计:
// 里程计缓冲区(线程安全)
std::mutex odometry_buffer_mutex_;
const unsigned int max_odometry_buffer_size_ = 100;
// 状态列表(线程安全)
std::mutex odometry_status_list_mutex_;
size_t odometry_status_list_max_size_ = 10;
4. 并发设计与性能优化
4.1 组件级并发
Apollo定位模块基于CyberRT的调度框架,每个Component运行在独立的调度组中。
4.1.1 DAG配置
RTK定位DAG(dag_streaming_rtk_localization.dag):
module_config {
    module_library : "modules/localization/rtk/librtk_localization_component.so"
    components {
      class_name : "RTKLocalizationComponent"
      config {
        name : "rtk_localization"
        config_file_path : "modules/localization/conf/rtk_localization.pb.txt"
        readers: [
          {
            channel: "/apollo/sensor/gnss/odometry"
            qos_profile: {
              depth : 10
            }
            pending_queue_size: 50
          }
        ]
      }
    }
}
MSF定位DAG(dag_streaming_msf_localization.dag):
module_config {
    module_library : "modules/localization/msf/libmsf_localization_component.so"
    components {
      class_name : "MSFLocalizationComponent"
      config {
        name : "msf_localization"
        flag_file_path : "modules/localization/conf/localization.conf"
        readers: [
          {
            channel: "/apollo/sensor/gnss/imu"
            qos_profile: {
              depth : 10
            }
            pending_queue_size: 50
          }
        ]
      }
    }
}
关键参数:
- qos_profile.depth:消息队列深度(10)
- pending_queue_size:待处理队列大小(50)
4.2 数据缓冲与同步
4.2.1 RTK定位的缓冲策略
// IMU消息缓冲(FIFO队列)
std::list<localization::CorrectedImu> imu_list_;
size_t imu_list_max_size_ = 50;
std::mutex imu_list_mutex_;
// GPS状态缓冲
std::list<drivers::gnss::InsStat> gps_status_list_;
size_t gps_status_list_max_size_ = 10;
std::mutex gps_status_list_mutex_;
特点:
- 使用std::list实现FIFO队列
- 限制最大大小防止内存溢出
- 互斥锁保护并发访问
4.2.2 NDT定位的缓冲策略
// 里程计缓冲(带Eigen内存对齐)
std::list<TimeStampPose, Eigen::aligned_allocator<TimeStampPose>>
    odometry_buffer_;
std::mutex odometry_buffer_mutex_;
const unsigned int max_odometry_buffer_size_ = 100;
// 状态列表
std::list<drivers::gnss::InsStat> odometry_status_list_;
std::mutex odometry_status_list_mutex_;
size_t odometry_status_list_max_size_ = 10;
特点:
- 使用Eigen内存对齐分配器(SSE/AVX优化)
- 更大的缓冲区(100个位姿)
- 支持时间查询和插值
4.2.3 MSF的定时器机制
// 独立定时器线程
std::unique_ptr<cyber::Timer> localization_timer_ = nullptr;
// 初始化(10ms周期,100Hz)
localization_timer_ = node_->CreateTimer(
    std::chrono::milliseconds(10),
    [this]() { OnLocalizationTimer(); }
);
优势:
- 解耦传感器触发和结果发布
- 保证固定频率输出
- 减少抖动
4.3 线程安全设计
4.3.1 互斥锁使用模式
// 典型的读写模式
void ImuCallback(const std::shared_ptr<localization::CorrectedImu> &imu_msg) {
  std::lock_guard<std::mutex> lock(imu_list_mutex_);
  imu_list_.push_back(*imu_msg);
  // 限制队列大小
  while (imu_list_.size() > imu_list_max_size_) {
    imu_list_.pop_front();
  }
}
bool FindMatchingIMU(const double gps_timestamp_sec, CorrectedImu *imu_msg) {
  std::lock_guard<std::mutex> lock(imu_list_mutex_);
  // 查找逻辑...
  for (auto it = imu_list_.begin(); it != imu_list_.end(); ++it) {
    // ...
  }
}
注意事项:
- 使用RAII风格的std::lock_guard
- 临界区尽可能小
- 避免嵌套锁
4.3.2 MSF的共享数据保护
class MSFLocalization {
 private:
  // IMU消息共享(多线程访问)
  std::shared_ptr<drivers::gnss::Imu> raw_imu_msg_;
  std::mutex mutex_imu_msg_;
};
// 写入
void OnRawImu(const std::shared_ptr<drivers::gnss::Imu> &imu_msg) {
  {
    std::lock_guard<std::mutex> lock(mutex_imu_msg_);
    raw_imu_msg_ = imu_msg;
  }
  localization_integ_.RawImuProcessFlu(*imu_msg);
}
// 读取
void OnLocalizationTimer() {
  std::shared_ptr<drivers::gnss::Imu> imu_msg;
  {
    std::lock_guard<std::mutex> lock(mutex_imu_msg_);
    imu_msg = raw_imu_msg_;
  }
  // 使用imu_msg...
}
4.4 性能优化技术
4.4.1 AVX加速
配置文件(localization.conf):
# if use avx to accelerate lidar localization
# need cpu to support avx intel intrinsics
# default: false
--if_use_avx=true
应用场景:
- 点云数据处理
- NDT变换计算
- 矩阵运算
4.4.2 点云降采样
# point cloud step
# type: int
# default: 2
--point_cloud_step=2
策略:
- 跳点采样(每隔N个点取一个)
- 减少计算量
- 牺牲少量精度
4.4.3 滤波器优化
# Lidar filter size
# type: int
# default: 11
--lidar_filter_size=17
作用:
- 平滑噪声
- 边缘保持
- 自适应大小
5. 配置与调度
5.1 配置文件系统
5.1.1 主配置文件
关键配置项:
# GNSS模式(0: bestgnss pose, 1: self gnss)
--gnss_mode=0
# 启用激光雷达定位
--enable_lidar_localization=true
# 定位模式(0: intensity, 1: altitude, 2: fusion)
--lidar_localization_mode=2
# Yaw对齐模式(0: off, 1: fusion, 2: fusion with multithread)
--lidar_yaw_align_mode=2
# 点云话题
--lidar_topic=/apollo/sensor/hesai40/compensator/PointCloud2
# 激光雷达外参文件
--lidar_extrinsics_file=modules/localization/msf/params/velodyne_params/velodyne64_novatel_extrinsics_example.yaml
# IMU坐标系(true: FLU, false: RFU)
--imu_coord_rfu=true
# UTM区域ID
--local_utm_zone_id=10
--if_utm_zone_id_from_folder=true
# 时间戳补偿
--enable_gps_imu_compensate=false
--trans_gpstime_to_utctime=false
# SINS状态检查
--integ_sins_state_check=true
--integ_sins_state_span_time=30.0
--integ_sins_state_pos_std=1.0
# 性能优化
--if_use_avx=true
--point_cloud_step=2
--lidar_filter_size=17
5.1.2 RTK配置
文件位置:modules/localization/conf/rtk_localization.pb.txt
# RTK特定配置
imu_to_ant_offset {
  x: 0.0
  y: 0.788
  z: 1.077
}
broadcast_tf_frame_id: "world"
broadcast_tf_child_frame_id: "localization"
5.2 启动流程
5.2.1 Launch文件
RTK定位启动(launch/rtk_localization.launch):
<cyber>
    <module>
        <name>localization</name>
        <dag_conf>modules/localization/dag/dag_streaming_rtk_localization.dag</dag_conf>
        <process_name>localization</process_name>
    </module>
</cyber>
MSF定位启动(launch/msf_localization.launch):
<cyber>
    <module>
        <name>localization</name>
        <dag_conf>modules/localization/dag/dag_streaming_msf_localization.dag</dag_conf>
        <process_name>localization</process_name>
    </module>
</cyber>
5.2.2 启动命令
# 启动RTK定位
cyber_launch start modules/localization/launch/rtk_localization.launch
# 启动MSF定位
cyber_launch start modules/localization/launch/msf_localization.launch
# 启动NDT定位
cyber_launch start modules/localization/launch/ndt_localization.launch
5.3 CyberRT调度
5.3.1 Component注册
// RTK定位组件注册
CYBER_REGISTER_COMPONENT(RTKLocalizationComponent);
// MSF定位组件注册
CYBER_REGISTER_COMPONENT(MSFLocalizationComponent);
// NDT定位组件注册
CYBER_REGISTER_COMPONENT(NDTLocalizationComponent);
5.3.2 消息订阅配置
RTK订阅配置:
bool RTKLocalizationComponent::InitIO() {
  // 订阅IMU数据
  corrected_imu_listener_ = node_->CreateReader<localization::CorrectedImu>(
      imu_topic_,
      [this](const std::shared_ptr<localization::CorrectedImu>& imu_msg) {
        localization_->ImuCallback(imu_msg);
      });
  // 订阅GPS状态
  gps_status_listener_ = node_->CreateReader<drivers::gnss::InsStat>(
      gps_status_topic_,
      [this](const std::shared_ptr<drivers::gnss::InsStat>& status_msg) {
        localization_->GpsStatusCallback(status_msg);
      });
  // 发布定位结果
  localization_talker_ = node_->CreateWriter<LocalizationEstimate>(
      localization_topic_);
  return true;
}
MSF订阅配置:
bool MSFLocalizationComponent::InitIO() {
  // 订阅激光雷达
  lidar_listener_ = node_->CreateReader<drivers::PointCloud>(
      lidar_topic_,
      [this](const std::shared_ptr<drivers::PointCloud>& msg) {
        localization_.OnPointCloud(msg);
      });
  // 订阅GNSS BestPose
  bestgnsspos_listener_ = node_->CreateReader<drivers::gnss::GnssBestPose>(
      bestgnsspos_topic_,
      [this](const std::shared_ptr<drivers::gnss::GnssBestPose>& msg) {
        localization_.OnGnssBestPose(msg);
      });
  // 订阅GNSS Heading
  gnss_heading_listener_ = node_->CreateReader<drivers::gnss::Heading>(
      gnss_heading_topic_,
      [this](const std::shared_ptr<drivers::gnss::Heading>& msg) {
        localization_.OnGnssHeading(msg);
      });
  return true;
}
6. 关键技术点
6.1 时间同步
6.1.1 GPS-IMU时间同步
RTK实现:
bool RTKLocalization::FindMatchingIMU(const double gps_timestamp_sec,
                                      CorrectedImu *imu_msg) {
  std::lock_guard<std::mutex> lock(imu_list_mutex_);
  if (imu_list_.empty()) {
    return false;
  }
  // 查找最接近的IMU数据
  auto imu_it = imu_list_.begin();
  for (; imu_it != imu_list_.end(); ++imu_it) {
    if (imu_it->header().timestamp_sec() >= gps_timestamp_sec) {
      break;
    }
  }
  // 时间差检查
  double time_diff = std::abs(imu_it->header().timestamp_sec() - gps_timestamp_sec);
  if (time_diff > gps_imu_time_diff_threshold_) {
    // 需要插值
    auto imu_it_1 = imu_it;
    --imu_it_1;
    return InterpolateIMU(*imu_it_1, *imu_it, gps_timestamp_sec, imu_msg);
  }
  *imu_msg = *imu_it;
  return true;
}
关键参数:
- gps_imu_time_diff_threshold_ = 0.02秒
- gps_time_delay_tolerance_ = 1.0秒
6.1.2 多传感器时间戳对齐
MSF使用缓冲区和时间戳查询:
// 时间戳对齐策略
bool QueryPoseFromBuffer(double time, Eigen::Affine3d* pose) {
  std::lock_guard<std::mutex> lock(odometry_buffer_mutex_);
  // 二分查找最接近的时间戳
  auto it = std::lower_bound(
      odometry_buffer_.begin(),
      odometry_buffer_.end(),
      time,
      [](const TimeStampPose& p, double t) {
        return p.timestamp < t;
      });
  // 插值计算
  // ...
}
6.2 坐标系变换
6.2.1 坐标系定义
- 世界坐标系(World Frame):UTM坐标系
- 车辆坐标系(Vehicle Frame):前-左-上(FLU)
- IMU坐标系(IMU Frame):根据传感器而定(FLU或RFU)
- 激光雷达坐标系(LiDAR Frame):传感器坐标系
6.2.2 外参标定
激光雷达外参:
# velodyne64_novatel_extrinsics_example.yaml
transform:
  translation:
    x: 0.0
    y: 0.0
    z: 1.80
  rotation:
    x: 0.0
    y: 0.0
    z: 0.0
    w: 1.0
IMU外参:
// 从文件加载
bool LoadImuVehicleExtrinsic(const std::string &file_path,
                             double *quat_qx, double *quat_qy,
                             double *quat_qz, double *quat_qw,
                             Eigen::Vector3d *translation);
// 应用变换
void CompensateImuVehicleExtrinsic(LocalizationEstimate *local_result) {
  Eigen::Quaterniond vehicle_quat =
      imu_vehicle_quat_ *
      Eigen::Quaterniond(local_result->pose().orientation().qw(),
                         local_result->pose().orientation().qx(),
                         local_result->pose().orientation().qy(),
                         local_result->pose().orientation().qz());
  local_result->mutable_pose()->mutable_orientation()->set_qx(vehicle_quat.x());
  local_result->mutable_pose()->mutable_orientation()->set_qy(vehicle_quat.y());
  local_result->mutable_pose()->mutable_orientation()->set_qz(vehicle_quat.z());
  local_result->mutable_pose()->mutable_orientation()->set_qw(vehicle_quat.w());
}
6.2.3 TF广播
void PublishPoseBroadcastTF(const LocalizationEstimate &localization) {
  geometry_msgs::TransformStamped tf;
  tf.header().set_timestamp_sec(localization.header().timestamp_sec());
  tf.header().set_frame_id(broadcast_tf_frame_id_);
  tf.set_child_frame_id(broadcast_tf_child_frame_id_);
  // 设置平移
  tf.mutable_transform()->mutable_translation()->set_x(
      localization.pose().position().x());
  tf.mutable_transform()->mutable_translation()->set_y(
      localization.pose().position().y());
  tf.mutable_transform()->mutable_translation()->set_z(
      localization.pose().position().z());
  // 设置旋转
  tf.mutable_transform()->mutable_rotation()->set_qx(
      localization.pose().orientation().qx());
  tf.mutable_transform()->mutable_rotation()->set_qy(
      localization.pose().orientation().qy());
  tf.mutable_transform()->mutable_rotation()->set_qz(
      localization.pose().orientation().qz());
  tf.mutable_transform()->mutable_rotation()->set_qw(
      localization.pose().orientation().qw());
  tf2_broadcaster_->SendTransform(tf);
}
6.3 异常处理
6.3.1 传感器故障检测
看门狗机制:
void RTKLocalization::RunWatchDog(double gps_timestamp) {
  if (!enable_watch_dog_) {
    return;
  }
  // 检查数据延迟
  double delay = cyber::Time::Now().ToSecond() - gps_timestamp;
  if (delay > service_delay_threshold) {
    monitor_logger_.ERROR(
        "Localization service delay: " + std::to_string(delay));
  }
  // 检查发布频率
  double publish_delay = gps_timestamp - last_reported_timestamp_sec_;
  if (publish_delay > 1.0 / localization_publish_freq_) {
    monitor_logger_.WARN(
        "Localization publish rate too low: " + std::to_string(1.0 / publish_delay));
  }
}
6.3.2 NDT匹配质量监控
void NDTLocalization::CheckMatchingQuality() {
  if (ndt_score_ > error_ndt_score_) {
    bad_score_count_++;
    if (bad_score_count_ > bad_score_count_threshold_) {
      AERROR << "NDT matching quality too bad, score: " << ndt_score_;
      localization_status_.set_fusion_status(
          LocalizationStatus::ERROR);
    }
  } else if (ndt_score_ > warnning_ndt_score_) {
    AWARN << "NDT matching quality degraded, score: " << ndt_score_;
    localization_status_.set_fusion_status(
        LocalizationStatus::WARNNING);
  } else {
    bad_score_count_ = 0;
    localization_status_.set_fusion_status(
        LocalizationStatus::OK);
  }
}
6.3.3 SINS状态检查
# 启用SINS状态检查
--integ_sins_state_check=true
# SINS状态检查时间窗口(秒)
--integ_sins_state_span_time=30.0
# SINS位置标准差阈值(米)
--integ_sins_state_pos_std=1.0
作用:
- 检测SINS(捷联惯导)发散
- 触发重新初始化
- 提升长时间运行稳定性
6.4 地图管理
6.4.1 地图加载
// 从文件夹加载UTM区域ID
bool LoadZoneIdFromFolder(const std::string &folder_path, int *zone_id) {
  std::string zone_id_file = folder_path + "/zone_id.txt";
  std::ifstream ifs(zone_id_file);
  if (!ifs.is_open()) {
    AERROR << "Failed to open zone id file: " << zone_id_file;
    return false;
  }
  ifs >> *zone_id;
  ifs.close();
  AINFO << "Loaded zone id: " << *zone_id;
  return true;
}
6.4.2 地图缓存
MSF使用地图池(Map Pool)管理:
class BaseMapPool {
 public:
  // 预加载地图节点
  void PreloadMapArea(const Eigen::Vector3d& location,
                      double resolution,
                      double radius);
  // 获取地图节点
  BaseMapNode* GetMapNode(const MapNodeIndex& index);
  // 释放远离车辆的地图节点
  void ReleaseDistantMapNodes(const Eigen::Vector3d& location,
                              double keep_radius);
 private:
  std::unordered_map<MapNodeIndex, BaseMapNode*> map_nodes_;
  std::mutex map_mutex_;
};
优化策略:
- 动态加载(按需)
- LRU缓存淘汰
- 预加载机制
7. 最佳实践与扩展
7.1 选择定位方法
| 定位方法 | 适用场景 | 精度 | 成本 | 实时性 | 
|---|---|---|---|---|
| RTK | 开阔场景,GPS信号良好 | 厘米级 | 低 | 高 | 
| MSF | 城市场景,需要高精度 | 厘米级 | 高 | 中 | 
| NDT | GPS拒止环境(隧道等) | 分米级 | 中 | 中 | 
推荐策略:
- 高速公路:RTK
- 城市道路:MSF
- 隧道/地下:NDT
7.2 性能调优
7.2.1 提升定位频率
# RTK发布频率(Hz)
--localization_publish_freq=100
# MSF定时器周期(ms)
localization_timer_ = node_->CreateTimer(
    std::chrono::milliseconds(10),  // 100Hz
    [this]() { OnLocalizationTimer(); }
);
7.2.2 降低计算负载
# 点云降采样
--point_cloud_step=4  # 从2增加到4
# 滤波器大小
--lidar_filter_size=11  # 从17减小到11
# 禁用多线程Yaw对齐
--lidar_yaw_align_mode=1  # 从2降到1
7.2.3 内存优化
# 使用有损地图(减少内存占用)
# 在地图制作时选择LossyMap而非LosslessMap
# 限制缓冲区大小
imu_list_max_size_ = 30;  // 从50减少到30
max_odometry_buffer_size_ = 50;  // 从100减少到50
7.3 添加新的定位方法
参照README中的说明,添加新定位实现(例如FooLocalization):
步骤1:在proto中添加类型
// localization_config.proto
enum LocalizationType {
  RTK = 0;
  MSF = 1;
  NDT = 2;
  FOO = 3;  // 新增
}
步骤2:创建实现目录和类
modules/localization/foo/
├── BUILD
├── foo_localization.h
├── foo_localization.cc
├── foo_localization_component.h
└── foo_localization_component.cc
// foo_localization.h
class FooLocalization : public LocalizationBase {
 public:
  FooLocalization();
  ~FooLocalization() = default;
  bool Init(const LocalizationConfig& config) override;
  void Process(const SensorData& data) override;
  void GetLocalization(LocalizationEstimate* localization) override;
 private:
  // 自定义实现...
};
// foo_localization_component.h
class FooLocalizationComponent final
    : public cyber::Component<YourInputMsgType> {
 public:
  bool Init() override;
  bool Proc(const std::shared_ptr<YourInputMsgType>& msg) override;
 private:
  std::unique_ptr<FooLocalization> localization_;
};
CYBER_REGISTER_COMPONENT(FooLocalizationComponent);
步骤3:注册定位方法
// localization.cc
void Localization::RegisterLocalizationMethods() {
  localization_factory_.Register(
      LocalizationConfig::RTK,
      []() -> LocalizationBase* { return new RTKLocalization(); });
  localization_factory_.Register(
      LocalizationConfig::MSF,
      []() -> LocalizationBase* { return new MSFLocalization(); });
  localization_factory_.Register(
      LocalizationConfig::NDT,
      []() -> LocalizationBase* { return new NDTLocalization(); });
  // 新增
  localization_factory_.Register(
      LocalizationConfig::FOO,
      []() -> LocalizationBase* { return new FooLocalization(); });
}
步骤4:创建DAG和Launch文件
// dag_streaming_foo_localization.dag
module_config {
    module_library : "modules/localization/foo/libfoo_localization_component.so"
    components {
      class_name : "FooLocalizationComponent"
      config {
        name : "foo_localization"
        config_file_path : "modules/localization/conf/foo_localization.pb.txt"
        readers: [
          {
            channel: "/your/input/channel"
            qos_profile: {
              depth : 10
            }
            pending_queue_size: 50
          }
        ]
      }
    }
}
<!-- foo_localization.launch -->
<cyber>
    <module>
        <name>localization</name>
        <dag_conf>modules/localization/dag/dag_streaming_foo_localization.dag</dag_conf>
        <process_name>localization</process_name>
    </module>
</cyber>
步骤5:编译和测试
# 编译
bash apollo.sh build
# 启动测试
cyber_launch start modules/localization/launch/foo_localization.launch
7.4 调试技巧
7.4.1 启用调试日志
# GNSS调试日志
--gnss_debug_log_flag=true
# LiDAR调试日志
--lidar_debug_log_flag=true
# NDT调试日志
--ndt_debug_log_flag=true
7.4.2 可视化工具
# 启动MSF可视化器
cyber_launch start modules/localization/launch/msf_localization_with_visualizer.launch
7.4.3 日志分析
# 查看定位结果
cyber_monitor
# 选择 /apollo/localization/pose
# 记录数据
cyber_recorder record -a -o localization_test.record
# 回放数据
cyber_recorder play -f localization_test.record
7.5 常见问题
问题1:RTK定位漂移
原因:
- GPS信号质量差
- IMU数据异常
- 时间同步问题
解决方案:
# 检查GPS状态
cyber_monitor
# 查看 /apollo/sensor/gnss/ins_stat
# 启用IMU补偿
--enable_gps_imu_compensate=true
# 调整时间同步阈值
gps_imu_time_diff_threshold_ = 0.05;  // 从0.02放宽到0.05
问题2:MSF初始化失败
原因:
- 地图未加载
- GNSS位置不准确
- UTM区域ID错误
解决方案:
# 检查地图路径
--map_dir=/apollo/modules/map/data/your_map
# 验证UTM区域
--if_utm_zone_id_from_folder=true
# 或手动设置
--local_utm_zone_id=50
# 启用GNSS初始化
--gnss_only_init=true
问题3:NDT匹配分数过高
原因:
- 地图与实际环境不匹配
- 初始位姿偏差大
- 动态物体干扰
解决方案:
# 调整匹配阈值
warnning_ndt_score_ = 2.0;  // 从1.0提高到2.0
error_ndt_score_ = 3.0;     // 从2.0提高到3.0
# 增加点云密度
--point_cloud_step=1  // 从2减小到1
# 使用多分辨率匹配
online_resolution_ = 1.0;  // 从2.0减小到1.0
总结
Apollo定位模块是一个高度模块化、支持多传感器融合的定位系统:
核心特性
- 三种定位方法:RTK、MSF、NDT,满足不同场景需求
- 组件化架构:基于CyberRT的Component设计,易于扩展
- 并发优化:多线程处理、缓冲队列、定时器机制
- 高精度:厘米级定位精度(MSF)
- 鲁棒性:异常检测、状态监控、传感器降级
技术亮点
- 时间同步:多传感器精确时间对齐
- 坐标系变换:灵活的外参标定系统
- 地图管理:动态加载、多分辨率支持
- 性能优化:AVX加速、降采样、滤波器优化
扩展性
- 清晰的接口设计(LocalizationBase)
- 工厂模式注册机制
- 配置驱动的参数系统
通过深入理解定位模块的架构和实现细节,开发者可以:
- 选择合适的定位方法
- 优化性能参数
- 添加自定义定位算法
- 调试和解决定位问题
定位模块为Apollo自动驾驶系统提供了坚实的基础,是实现安全、可靠自动驾驶的关键组件。
 
                   
                   
                   
                   
                             
       
           
                 
                 
                 
                 
                 
                
               
                 
                 
                 
                 
                
               
                 
                 扫一扫
扫一扫
                     
              
             
                   2699
					2699
					
 被折叠的  条评论
		 为什么被折叠?
被折叠的  条评论
		 为什么被折叠?
		 
		  到【灌水乐园】发言
到【灌水乐园】发言                                
		 
		 
    
   
    
   
             
            


 
            