第三部分:预测与定位算法详解

免责声明
本文档仅供学习和技术研究使用,内容基于 Apollo 开源项目的公开资料和代码分析整理而成。文档中的技术实现细节、架构设计等内容可能随 Apollo 项目更新而变化。使用本文档所述技术方案时,请以 Apollo 官方最新文档为准。

声明事项:

  • 本文档不构成任何商业使用建议
  • 涉及自动驾驶技术的应用需遵守当地法律法规
  • 作者不对因使用本文档内容产生的任何后果承担责任
  • Apollo 为百度公司注册商标,本文档为非官方技术解析

1. 预测模块架构

1.1 系统概述

Prediction模块负责预测其他道路参与者(车辆、行人、自行车等)的未来轨迹,为规划模块提供决策依据。

核心组件:

  • 位置: modules/prediction/prediction_component.h
  • 主类: PredictionComponent
  • 基类: cyber::Component<>

预测时长: 8秒(默认)
预测频率: 10Hz
时间分辨率: 0.1秒(80个轨迹点)

1.2 四层架构

┌─────────────────────────────────────────────────┐
│  Layer 1: Container(容器层)                    │
│  ├─ ObstaclesContainer(障碍物容器)              │
│  ├─ ADCTrajectoryContainer(自车轨迹容器)        │
│  └─ PoseContainer(位姿容器)                    │
└─────────────────┬───────────────────────────────┘
                  │
┌─────────────────▼───────────────────────────────┐
│  Layer 2: Scenario(场景层)                     │
│  ├─ ScenarioManager(场景管理器)                │
│  ├─ Cruise(巡航场景)                           │
│  ├─ Junction(路口场景)                         │
│  └─ Prioritization(优先级分类)                 │
│      ├─ IGNORE(忽略)                           │
│      ├─ CAUTION(警告)                          │
│      └─ NORMAL(普通)                           │
└─────────────────┬───────────────────────────────┘
                  │
┌─────────────────▼───────────────────────────────┐
│  Layer 3: Evaluator(评估层)                    │
│  9种评估器:                                      │
│  ├─ Cost Evaluator(成本评估)                   │
│  ├─ MLP Evaluator(多层感知机)                  │
│  ├─ Cruise MLP Evaluator(巡航MLP)              │
│  ├─ Junction MLP Evaluator(路口MLP)            │
│  ├─ Junction Map Evaluator(路口地图)           │
│  ├─ Semantic LSTM Evaluator(语义LSTM)⭐        │
│  ├─ VectorNet Evaluator(向量网络)              │
│  ├─ Jointly Prediction Planning Evaluator        │
│  └─ Multi-Agent Evaluator(多智能体)            │
└─────────────────┬───────────────────────────────┘
                  │
┌─────────────────▼───────────────────────────────┐
│  Layer 4: Predictor(预测器层)                  │
│  9种预测器:                                      │
│  ├─ Lane Sequence Predictor(车道序列)          │
│  ├─ Move Sequence Predictor(运动序列)          │
│  ├─ Free Movement Predictor(自由移动)          │
│  ├─ Regional Movement Predictor(区域移动)      │
│  ├─ Junction Predictor(路口)                   │
│  ├─ Interaction Predictor(交互)                │
│  ├─ Extrapolation Predictor(外推)              │
│  ├─ Single Lane Predictor(单车道)              │
│  └─ Empty Predictor(无预测)                    │
└──────────────────────────────────────────────────┘

1.3 容器层(Container)

1.3.1 ObstaclesContainer

位置: modules/prediction/container/obstacles/obstacles_container.h

功能:

  • 存储和管理感知模块输出的障碍物数据
  • 维护障碍物历史信息(位置、速度、加速度)
  • 提供障碍物查询接口

Obstacle类 (container/obstacles/obstacle.h):

class Obstacle {
 public:
  // 基本信息
  int id() const;                           // 障碍物ID
  PerceptionObstacle::Type type() const;    // 类型(车辆/行人/自行车)

  // 特征访问
  const Feature& latest_feature() const;    // 最新特征
  Feature* mutable_latest_feature();        // 可修改的最新特征
  int history_size() const;                 // 历史帧数
  const Feature& feature(int i) const;      // 第i帧特征

  // 状态判断
  bool IsOnLane() const;                    // 是否在车道上
  bool IsNearJunction() const;              // 是否接近路口
  bool IsInJunction() const;                // 是否在路口内
  bool IsStill() const;                     // 是否静止
  bool IsPedestrian() const;                // 是否行人
  bool IsVehicle() const;                   // 是否车辆

  // 卡尔曼滤波
  const KalmanFilter<double, 6, 2, 0>& kf() const;  // 获取卡尔曼滤波器

  // 预测结果
  int NumOfPredictedTrajectory() const;     // 预测轨迹数量
  const Trajectory& trajectory(int i) const; // 第i条轨迹

 private:
  int id_;
  std::deque<Feature> feature_history_;     // 特征历史队列
  KalmanFilter<double, 6, 2, 0> kf_;        // 卡尔曼滤波器
};

卡尔曼滤波器配置:

// 状态向量 [x, y, vx, vy, ax, ay]
// 观测向量 [x, y]
KalmanFilter<double, 6, 2, 0> kf_;

// 参数配置(prediction_gflags.h)
FLAGS_enable_kf_tracking: true
FLAGS_q_var: 0.01  // 过程噪声方差
FLAGS_r_var: 0.1   // 观测噪声方差
FLAGS_p_var: 0.1   // 初始协方差
1.3.2 ADCTrajectoryContainer

位置: modules/prediction/container/adc_trajectory/adc_trajectory_container.h

功能:

  • 存储自动驾驶车辆(ADC)的规划轨迹
  • 用于交互预测(Jointly Prediction Planning)
1.3.3 PoseContainer

位置: modules/prediction/container/pose/pose_container.h

功能:

  • 存储车辆定位信息
  • 提供坐标转换服务

1.4 场景层(Scenario)

1.4.1 场景管理器

ScenarioManager (scenario/scenario_manager.h):

支持场景:

enum ScenarioType {
  CRUISE,      // 巡航场景(车道保持、跟车)
  JUNCTION     // 路口场景(左转、右转、直行)
};

场景判断逻辑:

// 判断是否接近路口
if (distance_to_junction < FLAGS_junction_distance_threshold) {
  return ScenarioType::JUNCTION;
} else {
  return ScenarioType::CRUISE;
}

// 典型参数
FLAGS_junction_distance_threshold: 20.0
1.4.2 优先级分类

ObstaclesPrioritizer (scenario/prioritization/obstacles_prioritizer.h):

三种优先级:

enum PriorityType {
  IGNORE,    // 忽略 - 不影响主车轨迹
  CAUTION,   // 警告 - 可能与主车交互
  NORMAL     // 普通 - 默认优先级
};

分类规则:

IGNORE:
  - 距离主车 > caution_distance_threshold
  - 在主车后方且速度慢于主车

CAUTION:
  - 距离主车 < caution_distance_threshold
  - 在主车前方或侧方
  - 速度与主车相近
  - 可能变道到主车车道

NORMAL:
  - 其他情况

典型参数:

FLAGS_caution_distance_threshold: 50.0米
FLAGS_interaction_distance_threshold: 100.0

2. 评估器算法详解

2.1 评估器管理器

EvaluatorManager (evaluator/evaluator_manager.h):

功能:

  • 根据障碍物类型、状态、场景选择合适的评估器
  • 管理模型加载和推理
  • 协调多个评估器的执行

2.2 九种评估器详解

2.2.1 Cost Evaluator(成本评估器)

位置: modules/prediction/evaluator/vehicle/cost_evaluator.h

算法: 基于成本函数的路径概率计算

成本函数:

Cost = w1 * CostToLane +
       w2 * CostToLaneChange +
       w3 * CostToCollision +
       w4 * CostToCenterLine

// 概率计算
Probability = exp(-Cost / temperature) / Z

其中:
  Z: 归一化因子
  temperature: 温度参数(控制概率分布尖锐度)

特点:

  • 计算简单快速
  • 适合实时性要求高的场景
  • 不依赖深度学习模型
2.2.2 MLP Evaluator(多层感知机评估器)

位置: modules/prediction/evaluator/vehicle/mlp_evaluator.h

特征维度:

障碍物特征: 22维
  - 位置 (x, y)
  - 速度 (vx, vy, v)
  - 加速度 (ax, ay, a)
  - 航向角、航向角速度
  - 尺寸 (length, width, height)
  - 距离车道中心线距离
  - 相对主车位置
  - ...

车道特征: 40维
  - 左车道特征 (20维)
  - 右车道特征 (20维)
  - 每个车道:
    - 车道类型
    - 车道宽度
    - 车道曲率
    - 到车道中心线距离
    - ...

网络结构:

输入层: 62维 (22 + 40)
隐藏层1: 128节点 + ReLU
隐藏层2: 64节点 + ReLU
输出层: 12维 (3个候选车道 × 4个概率)
  - 保持当前车道概率
  - 左变道概率
  - 右变道概率
  - 其他概率

模型文件: data/mlp_vehicle_model.bin

2.2.3 Cruise MLP Evaluator(巡航MLP评估器)

位置: modules/prediction/evaluator/vehicle/cruise_mlp_evaluator.h

特征规模:

障碍物特征: 23 + 5×9 = 68维
  - 基础特征: 23维
  - 车道序列特征: 5条车道 × 9维/车道

交互特征: 8维
  - 与周围障碍物的相对位置
  - 相对速度
  - 时间碰撞时间(TTC)

单车道特征: 4维 × 20个车道点 = 80维
  - 每个点: (s, l, heading, curvature)

模型:

Go模型: data/cruise_go_vehicle_model.pt
  - 用于判断是否继续前进
  - 输出: 前进概率

Cut-in模型: data/cruise_cutin_vehicle_model.pt
  - 用于判断是否会切入主车车道
  - 输出: 切入概率

框架: PyTorch JIT模型

2.2.4 Junction MLP Evaluator(路口MLP评估器)

位置: modules/prediction/evaluator/vehicle/junction_mlp_evaluator.h

专用场景: 交叉路口场景

特点:

  • 考虑多条可能路径(左转、右转、直行)
  • 结合地图拓扑信息
  • 输出每条路径的概率

模型: data/junction_mlp_vehicle_model.pt

2.2.5 Junction Map Evaluator(路口地图评估器)

位置: modules/prediction/evaluator/vehicle/junction_map_evaluator.h

算法: 基于语义地图的CNN模型

输入:

语义地图: 224×224×3
  - 道路: 灰色
  - 车道线: 白色/黄色
  - 人行横道: 蓝色
  - 路口: 绿色

网络: ResNet-18 + LSTM

优先级: 仅处理CAUTION级别障碍物

模型: data/junction_map_vehicle_model.pt


3. LSTM轨迹预测算法

3.1 Semantic LSTM Evaluator

位置: modules/prediction/evaluator/vehicle/semantic_lstm_evaluator.h

核心算法: LSTM + 语义地图

3.1.1 算法流程

代码位置: semantic_lstm_evaluator.cc 行45-150

bool SemanticLSTMEvaluator::Evaluate(
    Obstacle* obstacle_ptr,
    ObstaclesContainer* obstacles_container) {

  // ===== 步骤1: 获取语义地图 (224×224×3) =====
  cv::Mat feature_map;
  if (!semantic_map_->GetMapById(id, &feature_map)) {
    return false;
  }

  // ===== 步骤2: 图像预处理 =====
  // 2.1 BGR → RGB转换
  cv::cvtColor(feature_map, feature_map, cv::COLOR_BGR2RGB);

  // 2.2 归一化到[0, 1]
  cv::Mat img_float;
  feature_map.convertTo(img_float, CV_32F, 1.0 / 255);

  // 2.3 转换为PyTorch张量
  torch::Tensor img_tensor = torch::from_blob(
      img_float.data, {1, 224, 224, 3});
  img_tensor = img_tensor.permute({0, 3, 1, 2});  // NHWC → NCHW

  // 2.4 ImageNet标准化
  img_tensor[0][0] = img_tensor[0][0].sub(0.485).div(0.229);  // R通道
  img_tensor[0][1] = img_tensor[0][1].sub(0.456).div(0.224);  // G通道
  img_tensor[0][2] = img_tensor[0][2].sub(0.406).div(0.225);  // B通道

  // ===== 步骤3: 提取历史轨迹 (20帧) =====
  std::vector<std::pair<double, double>> pos_history(20, {0.0, 0.0});
  if (!ExtractObstacleHistory(obstacle_ptr, &pos_history)) {
    return false;
  }

  // ===== 步骤4: 构建LSTM输入 =====
  torch::Tensor obstacle_pos = torch::zeros({1, 20, 2});
  torch::Tensor obstacle_pos_step = torch::zeros({1, 20, 2});

  for (int i = 0; i < 20; ++i) {
    // 位置(相对于当前位置)
    obstacle_pos[0][19 - i][0] = pos_history[i].first;
    obstacle_pos[0][19 - i][1] = pos_history[i].second;

    // 位置步长(速度)
    if (i < 19 && pos_history[i].first != 0.0) {
      obstacle_pos_step[0][19 - i][0] =
          pos_history[i].first - pos_history[i + 1].first;
      obstacle_pos_step[0][19 - i][1] =
          pos_history[i].second - pos_history[i + 1].second;
    }
  }

  // ===== 步骤5: 模型推理 =====
  std::vector<void*> input_buffers{
      img_tensor.data_ptr<float>(),
      obstacle_pos.data_ptr<float>(),
      obstacle_pos_step.data_ptr<float>()
  };
  std::vector<void*> output_buffers{
      torch_default_output_tensor_.data_ptr<float>()
  };

  // 根据障碍物类型选择模型
  if (obstacle_ptr->IsPedestrian()) {
    model_manager_.SelectModel(
        device_, ObstacleConf::SEMANTIC_LSTM_EVALUATOR,
        PerceptionObstacle::PEDESTRIAN)->Inference(
            input_buffers, 3, &output_buffers, 1);
  } else {
    model_manager_.SelectModel(
        device_, ObstacleConf::SEMANTIC_LSTM_EVALUATOR,
        PerceptionObstacle::VEHICLE)->Inference(
            input_buffers, 3, &output_buffers, 1);
  }

  // 输出张量: {1, 30, 2}
  at::Tensor torch_output_tensor = torch::from_blob(
      output_buffers[0], {1, 30, 2});

  // ===== 步骤6: 坐标变换(相对坐标 → 世界坐标) =====
  double pos_x = latest_feature_ptr->position().x();
  double pos_y = latest_feature_ptr->position().y();
  double heading = latest_feature_ptr->velocity_heading();

  Trajectory* trajectory = latest_feature_ptr->add_predicted_trajectory();
  trajectory->set_probability(1.0);

  for (int i = 0; i < 30; ++i) {
    double prev_x = pos_x;
    double prev_y = pos_y;
    if (i > 0) {
      const auto& last_point =
          trajectory->trajectory_point(i - 1).path_point();
      prev_x = last_point.x();
      prev_y = last_point.y();
    }

    // 获取相对偏移
    double dx = torch_output[0][i][0];
    double dy = torch_output[0][i][1];

    // 旋转到世界坐标系
    Vec2d offset(dx, dy);
    Vec2d rotated_offset = offset.rotate(heading);

    // 计算世界坐标
    double point_x = prev_x + rotated_offset.x();
    double point_y = prev_y + rotated_offset.y();

    // 添加到轨迹
    TrajectoryPoint* point = trajectory->add_trajectory_point();
    point->mutable_path_point()->set_x(point_x);
    point->mutable_path_point()->set_y(point_y);
    point->set_relative_time(i * 0.1);  // 0.1秒间隔
  }

  return true;
}
3.1.2 网络结构

LSTM网络架构:

┌─────────────────────────────────────────────────┐
│  输入层                                          │
├─────────────────────────────────────────────────┤
│  1. 语义地图: [1, 3, 224, 224]                   │
│     └─ CNN特征提取器(ResNet-18)                │
│        └─ 输出: [1, 512]                         │
│                                                  │
│  2. 历史位置: [1, 20, 2]                         │
│     └─ 位置编码层                                │
│        └─ 输出: [1, 20, 64]                      │
│                                                  │
│  3. 历史位置步长: [1, 20, 2]                     │
│     └─ 速度编码层                                │
│        └─ 输出: [1, 20, 64]                      │
└─────────────────────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────┐
│  特征融合层                                       │
│  └─ 拼接: [1, 512 + 20×64 + 20×64]              │
│     └─ 全连接: [1, 256]                          │
└─────────────────────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────┐
│  LSTM层                                          │
│  ├─ LSTM单元数: 128                              │
│  ├─ 层数: 2                                      │
│  ├─ Dropout: 0.3                                 │
│  └─ 输出: [1, 128](隐藏状态)                   │
└─────────────────────────────────────────────────┘
                       │
                       ▼
┌─────────────────────────────────────────────────┐
│  解码器层                                         │
│  ├─ 全连接1: [1, 128] → [1, 256]                │
│  ├─ ReLU激活                                     │
│  ├─ 全连接2: [1, 256] → [1, 60]                 │
│  └─ Reshape: [1, 30, 2]                          │
└─────────────────────────────────────────────────┘
                       │
                       ▼
               输出: 30个轨迹点
               每个点: (Δx, Δy)
3.1.2.1 LSTM网络深度解析

LSTM单元内部结构

Long Short-Term Memory (LSTM)通过门控机制解决RNN的梯度消失问题:

LSTM单元包含3个门 + 1个细胞状态:
┌─────────────────────────────────┐
│  输入门 (Input Gate)              │
│  i_t = σ(W_i[h_{t-1}, x_t] + b_i)│
└─────────────────────────────────┘
┌─────────────────────────────────┐
│  遗忘门 (Forget Gate)             │
│  f_t = σ(W_f[h_{t-1}, x_t] + b_f)│
└─────────────────────────────────┘
┌─────────────────────────────────┐
│  输出门 (Output Gate)             │
│  o_t = σ(W_o[h_{t-1}, x_t] + b_o)│
└─────────────────────────────────┘
┌─────────────────────────────────┐
│  候选细胞状态                      │
│  C̃_t = tanh(W_C[h_{t-1}, x_t]+b_C)│
└─────────────────────────────────┘

细胞状态更新:
C_t = f_t ⊙ C_{t-1} + i_t ⊙ C̃_t

隐藏状态更新:
h_t = o_t ⊙ tanh(C_t)

其中:
  σ: sigmoid函数 (0-1范围,作为门控信号)
  ⊙: 逐元素乘法
  [h_{t-1}, x_t]: 拼接操作

Apollo LSTM配置

# PyTorch模型定义
class SemanticLSTMModel(nn.Module):
    def __init__(self):
        super(SemanticLSTMModel, self).__init__()

        # CNN特征提取器 (ResNet-18预训练)
        self.cnn = models.resnet18(pretrained=True)
        self.cnn.fc = nn.Identity()  # 移除最后的分类层
        # 输出: [batch, 512]

        # 位置编码器
        self.pos_encoder = nn.Linear(2, 64)
        # 输入: [batch, seq_len=20, 2]
        # 输出: [batch, 20, 64]

        # 速度编码器
        self.vel_encoder = nn.Linear(2, 64)
        # 输入: [batch, seq_len=20, 2]
        # 输出: [batch, 20, 64]

        # 特征融合层
        self.fusion = nn.Linear(512 + 20*64 + 20*64, 256)
        # 输入: [batch, 512 + 1280 + 1280] = [batch, 3072]
        # 输出: [batch, 256]

        # LSTM层
        self.lstm = nn.LSTM(
            input_size=256,
            hidden_size=128,
            num_layers=2,
            batch_first=True,
            dropout=0.3
        )
        # 输入: [batch, seq_len=1, 256]
        # 输出: [batch, 1, 128]

        # 解码器
        self.decoder = nn.Sequential(
            nn.Linear(128, 256),
            nn.ReLU(),
            nn.Linear(256, 60)  # 30个点 × 2个坐标
        )
        # 输出: [batch, 60] → reshape → [batch, 30, 2]

    def forward(self, img, pos_history, vel_history):
        # Step 1: CNN特征提取
        cnn_feat = self.cnn(img)  # [batch, 512]

        # Step 2: 位置和速度编码
        pos_feat = self.pos_encoder(pos_history)  # [batch, 20, 64]
        vel_feat = self.vel_encoder(vel_history)  # [batch, 20, 64]

        # Step 3: 特征展平和拼接
        pos_feat_flat = pos_feat.view(batch, -1)  # [batch, 1280]
        vel_feat_flat = vel_feat.view(batch, -1)  # [batch, 1280]

        combined = torch.cat([cnn_feat, pos_feat_flat, vel_feat_flat], dim=1)
        # [batch, 3072]

        # Step 4: 特征融合
        fused_feat = self.fusion(combined)  # [batch, 256]
        fused_feat = fused_feat.unsqueeze(1)  # [batch, 1, 256]

        # Step 5: LSTM编码
        lstm_out, (h_n, c_n) = self.lstm(fused_feat)
        lstm_out = lstm_out[:, -1, :]  # 取最后时刻的输出 [batch, 128]

        # Step 6: 解码为轨迹
        trajectory = self.decoder(lstm_out)  # [batch, 60]
        trajectory = trajectory.view(batch, 30, 2)  # [batch, 30, 2]

        return trajectory

训练损失函数

Apollo采用**加权均方误差(Weighted MSE)**作为损失函数:

def weighted_mse_loss(pred, target, time_weights):
    """
    Args:
        pred: 预测轨迹 [batch, 30, 2]
        target: 真实轨迹 [batch, 30, 2]
        time_weights: 时间权重 [30]

    Returns:
        loss: 标量损失值
    """
    # 计算每个点的MSE
    mse_per_point = torch.sum((pred - target) ** 2, dim=2)  # [batch, 30]

    # 应用时间权重 (越远的点权重越大)
    # time_weights = [0.5, 0.6, 0.7, ..., 3.0]
    weighted_mse = mse_per_point * time_weights  # [batch, 30]

    # 平均损失
    loss = torch.mean(weighted_mse)

    return loss

# 时间权重设计
time_weights = torch.linspace(0.5, 3.0, 30)
# 原理: 短期预测(0.5-1.0秒)相对容易,给予较低权重
#       长期预测(2.0-3.0秒)更难但更重要,给予较高权重

完整训练代码示例

# 训练配置
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.5)

num_epochs = 100
batch_size = 32

# 数据增强
augmentation = A.Compose([
    A.RandomBrightnessContrast(p=0.5),
    A.GaussianBlur(p=0.3),
    A.RandomRotation(degrees=5)
])

# 训练循环
for epoch in range(num_epochs):
    model.train()
    total_loss = 0.0

    for batch_idx, (img, pos_hist, vel_hist, target_traj) in enumerate(train_loader):
        # 数据移到GPU
        img = img.to(device)
        pos_hist = pos_hist.to(device)
        vel_hist = vel_hist.to(device)
        target_traj = target_traj.to(device)

        # 前向传播
        pred_traj = model(img, pos_hist, vel_hist)

        # 计算损失
        loss = weighted_mse_loss(pred_traj, target_traj, time_weights)

        # 反向传播
        optimizer.zero_grad()
        loss.backward()

        # 梯度裁剪 (防止梯度爆炸)
        torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=5.0)

        # 参数更新
        optimizer.step()

        total_loss += loss.item()

    # 学习率衰减
    scheduler.step()

    # 打印训练信息
    avg_loss = total_loss / len(train_loader)
    print(f"Epoch {epoch+1}/{num_epochs}, Loss: {avg_loss:.4f}")

    # 验证
    if (epoch + 1) % 5 == 0:
        model.eval()
        val_ade, val_fde = evaluate(model, val_loader)
        print(f"Validation ADE: {val_ade:.2f}m, FDE: {val_fde:.2f}m")

评价指标

def evaluate(model, dataloader):
    """
    计算平均位移误差(ADE)和最终位移误差(FDE)
    """
    model.eval()
    total_ade = 0.0
    total_fde = 0.0
    num_samples = 0

    with torch.no_grad():
        for img, pos_hist, vel_hist, target_traj in dataloader:
            img = img.to(device)
            pos_hist = pos_hist.to(device)
            vel_hist = vel_hist.to(device)
            target_traj = target_traj.to(device)

            # 预测
            pred_traj = model(img, pos_hist, vel_hist)

            # ADE: 所有时间步的平均欧氏距离
            # ADE = (1/T) * Σ||pred_t - gt_t||
            diff = pred_traj - target_traj  # [batch, 30, 2]
            dist = torch.sqrt(diff[:, :, 0]**2 + diff[:, :, 1]**2)  # [batch, 30]
            ade = torch.mean(dist)  # 标量

            # FDE: 最后一个时间步的欧氏距离
            # FDE = ||pred_T - gt_T||
            fde = torch.sqrt(
                (pred_traj[:, -1, 0] - target_traj[:, -1, 0])**2 +
                (pred_traj[:, -1, 1] - target_traj[:, -1, 1])**2
            )
            fde = torch.mean(fde)

            total_ade += ade.item() * img.size(0)
            total_fde += fde.item() * img.size(0)
            num_samples += img.size(0)

    avg_ade = total_ade / num_samples
    avg_fde = total_fde / num_samples

    return avg_ade, avg_fde

# 典型性能指标 (在验证集上):
# - 车辆预测:
#   ADE: 1.2-1.8米
#   FDE: 2.5-4.0米
# - 行人预测:
#   ADE: 0.8-1.2米
#   FDE: 1.5-2.5米

数据集准备

# 数据格式
{
    "obstacle_id": 12345,
    "timestamp": 1234567890.123,
    "type": "VEHICLE",  # or "PEDESTRIAN"

    # 历史轨迹 (2秒,20帧,每帧0.1秒)
    "history": [
        {"x": 100.0, "y": 50.0, "t": -2.0},
        {"x": 100.5, "y": 50.1, "t": -1.9},
        ...
        {"x": 110.0, "y": 52.0, "t": 0.0}
    ],

    # 未来轨迹 (3秒,30帧)
    "future": [
        {"x": 110.5, "y": 52.1, "t": 0.1},
        {"x": 111.0, "y": 52.2, "t": 0.2},
        ...
        {"x": 125.0, "y": 55.0, "t": 3.0}
    ],

    # 语义地图 (224×224 RGB图像)
    "semantic_map_path": "/path/to/map_12345.png"
}

# 语义地图生成
def generate_semantic_map(obstacle_pos, map_range=50.0, resolution=224):
    """
    生成以障碍物为中心的语义地图

    Args:
        obstacle_pos: (x, y) 障碍物位置
        map_range: 地图范围 (±50米)
        resolution: 图像分辨率 224×224

    Returns:
        semantic_map: RGB图像 [224, 224, 3]
    """
    img = np.zeros((resolution, resolution, 3), dtype=np.uint8)

    # 颜色编码
    ROAD_COLOR = [128, 128, 128]       # 灰色
    LANE_LINE_COLOR = [255, 255, 255]  # 白色
    CROSSWALK_COLOR = [255, 255, 0]    # 黄色
    VEHICLE_COLOR = [0, 0, 255]        # 蓝色
    PEDESTRIAN_COLOR = [255, 0, 0]     # 红色

    # 查询HD地图,绘制道路元素
    lanes = hd_map.query_lanes(obstacle_pos, map_range)
    for lane in lanes:
        draw_lane(img, lane, ROAD_COLOR)
        draw_lane_boundary(img, lane, LANE_LINE_COLOR)

    crosswalks = hd_map.query_crosswalks(obstacle_pos, map_range)
    for cw in crosswalks:
        draw_crosswalk(img, cw, CROSSWALK_COLOR)

    # 绘制周围障碍物
    nearby_obs = perception.query_obstacles(obstacle_pos, map_range)
    for obs in nearby_obs:
        if obs.type == "VEHICLE":
            draw_obstacle(img, obs, VEHICLE_COLOR)
        elif obs.type == "PEDESTRIAN":
            draw_obstacle(img, obs, PEDESTRIAN_COLOR)

    return img

模型推理优化

// C++推理代码优化技巧
// modules/prediction/evaluator/vehicle/semantic_lstm_evaluator.cc

// 技巧1: 批处理推理
std::vector<Obstacle*> batch_obstacles;
batch_obstacles.reserve(32);  // 批大小32

for (auto& obs : obstacles) {
  batch_obstacles.push_back(&obs);

  if (batch_obstacles.size() == 32) {
    // 批量推理
    BatchInference(batch_obstacles);
    batch_obstacles.clear();
  }
}

// 处理剩余障碍物
if (!batch_obstacles.empty()) {
  BatchInference(batch_obstacles);
}

// 技巧2: 模型量化 (FP32 → FP16)
// 在模型转换时使用:
// torch.jit.save(model, "model.pt", {"use_fp16": True})

// 技巧3: CUDA流并发
cudaStream_t stream1, stream2;
cudaStreamCreate(&stream1);
cudaStreamCreate(&stream2);

// 交替使用两个流
InferenceAsync(obstacles_batch1, stream1);
InferenceAsync(obstacles_batch2, stream2);

cudaStreamSynchronize(stream1);
cudaStreamSynchronize(stream2);

// 性能对比:
// - 单个推理: 15-20ms
// - 批处理(batch=32): 平均2-3ms/样本
// - 量化+批处理: 平均1-2ms/样本
3.1.3 模型文件

车辆模型:

GPU版本: data/semantic_lstm_vehicle_model.pt
CPU版本: data/semantic_lstm_vehicle_cpu_model.pt

行人模型:

GPU版本: data/semantic_lstm_pedestrian_model.pt
CPU版本: data/semantic_lstm_pedestrian_cpu_model.pt

模型大小: ~20MB

推理时间: 10-20ms (GPU) / 50-100ms (CPU)

3.1.4 坐标系统

相对坐标系定义:

            ↑ y (左侧)
            │
            │
  ──────────●────────── → x (前方)
    (当前障碍物位置)

坐标变换:

// 1. 世界坐标 → 障碍物相对坐标
std::pair<double, double> WorldCoordToObjCoord(
    std::pair<double, double> input_world_coord,
    std::pair<double, double> obj_world_coord,
    double obj_world_angle) {

  // 计算相对位移
  double x_diff = input_world_coord.first - obj_world_coord.first;
  double y_diff = input_world_coord.second - obj_world_coord.second;

  // 转换到极坐标
  double rho = sqrt(x_diff*x_diff + y_diff*y_diff);
  double theta = atan2(y_diff, x_diff) - obj_world_angle;

  // 转回直角坐标(障碍物坐标系)
  return {cos(theta)*rho, sin(theta)*rho};
}

// 2. 相对偏移 → 世界坐标
Vec2d offset(dx, dy);
Vec2d rotated_offset = offset.rotate(heading);
world_x = current_x + rotated_offset.x();
world_y = current_y + rotated_offset.y();

3.2 VectorNet Evaluator

位置: modules/prediction/evaluator/vehicle/vectornet_evaluator.h

核心思想: 将地图元素表示为向量集合

3.2.1 VectorNet处理

VectorNet类 (pipeline/vector_net.h):

class VectorNet {
 public:
  // 地图元素类型
  enum ATTRIBUTE_TYPE {
    ROAD = 0,                    // 道路
    LANE_DOTTED_YELLOW = 1,      // 黄虚线
    LANE_DOTTED_WHITE = 2,       // 白虚线
    LANE_SOLID_YELLOW = 3,       // 黄实线
    LANE_SOLID_WHITE = 4,        // 白实线
    JUNCTION = 5,                // 路口
    CROSSWALK = 6,               // 人行横道
    BOUNDARY = 7,                // 边界
    STOP_SIGN = 8,               // 停止标志
    UNKNOWN = -1
  };

  // 查询地图向量
  bool query(
      const PointENU& center_point,       // 中心点
      const double obstacle_phi,          // 障碍物航向
      FeatureVector* feature_ptr,         // 输出特征向量
      PidVector* p_id_ptr);               // 输出多段线ID

  // 获取特定类型元素
  void GetRoads(const PointENU& center_point, ...);
  void GetLanes(const PointENU& center_point, ...);
  void GetJunctions(const PointENU& center_point, ...);
  void GetCrosswalks(const PointENU& center_point, ...);
};

向量表示:

每个地图元素表示为一组向量:
  向量 = (start_point, end_point, attribute)

例如车道线:
  向量1: (P0, P1, LANE_SOLID_WHITE)
  向量2: (P1, P2, LANE_SOLID_WHITE)
  向量3: (P2, P3, LANE_SOLID_WHITE)
  ...

网络结构:

VectorNet = 子图网络 + 全局交互网络

子图网络:
  - 处理每个地图元素的向量序列
  - 使用GRU聚合向量特征

全局交互网络:
  - 处理所有子图特征
  - 使用Self-Attention捕获交互

3.3 Jointly Prediction Planning Evaluator

位置: modules/prediction/evaluator/vehicle/jointly_prediction_planning_evaluator.h

创新点: 同时考虑ADC规划轨迹和障碍物预测

输入:

1. 障碍物历史轨迹: [1, 20, 2]
2. ADC规划轨迹: [1, 80, 2]
3. VectorNet地图特征: [1, N, 128]

网络结构:

障碍物编码器 → LSTM → [1, 128]
       ↓
ADC轨迹编码器 → LSTM → [1, 128]  → 拼接 → 解码器 → 轨迹
       ↓
地图特征编码器 → GNN → [1, 128]

优势:

  • 考虑主车意图
  • 更准确的交互预测
  • 适合复杂场景

模型: data/jointly_prediction_planning_vehicle_model.pt


4. 定位模块架构

4.1 系统概述

Localization模块提供车辆的精确位姿估计,包括位置、速度、加速度、航向角等信息。

定位精度要求:

横向误差: < 0.1m (RMS)
纵向误差: < 0.2m (RMS)
航向误差: < 0.5° (RMS)

三种定位方法:

方法 传感器 精度 适用场景
RTK GPS + IMU 厘米级 开阔场景、GPS信号良好
MSF GPS + IMU + LiDAR 厘米级 全场景、高精度要求
NDT LiDAR 分米级 GPS失效、隧道/地库

4.2 定位消息格式

LocalizationEstimate (localization.proto):

message LocalizationEstimate {
  Header header = 1;
  Pose pose = 2;
  Uncertainty uncertainty = 3;
  double measurement_time = 4;      // 测量时间戳
  MeasureState msf_status = 5;      // MSF状态
  MeasureState lidar_status = 6;    // LiDAR状态
  MeasureState fusion_status = 7;   // 融合状态
}

message Pose {
  // 位置(地图坐标系)
  Point3D position = 1;             // (x, y, z)

  // 姿态(四元数)
  Quaternion orientation = 2;       // (qw, qx, qy, qz)

  // 线速度(地图坐标系)
  Point3D linear_velocity = 3;      // (vx, vy, vz)

  // 线加速度(地图坐标系)
  Point3D linear_acceleration = 4;  // (ax, ay, az)

  // 角速度(地图坐标系)
  Point3D angular_velocity = 5;     // (ωx, ωy, ωz)

  // 航向角(弧度)
  double heading = 6;

  // 线加速度(车体坐标系)
  Point3D linear_acceleration_vrf = 7;

  // 角速度(车体坐标系)
  Point3D angular_velocity_vrf = 8;

  // 欧拉角(roll, pitch, yaw)
  EulerAngles euler_angles = 9;
}

message Uncertainty {
  // 位置不确定性
  Point3D position_std_dev = 1;     // 标准差

  // 姿态不确定性
  Point3D orientation_std_dev = 2;

  // 速度不确定性
  Point3D linear_velocity_std_dev = 3;

  // 加速度不确定性
  Point3D linear_acceleration_std_dev = 4;

  // 角速度不确定性
  Point3D angular_velocity_std_dev = 5;
}

5. RTK定位算法

5.1 RTK定位原理

RTK (Real-Time Kinematic): 实时动态差分定位

原理:

基准站 (已知精确坐标)
    ↓ 差分改正数
GPS卫星 → 移动站 (车辆) → 高精度定位结果
    ↓
接收原始观测值

核心组件: modules/localization/rtk/rtk_localization.h

5.2 算法流程

RTKLocalization类:

class RTKLocalization {
 public:
  // 主要回调函数
  void GpsCallback(const Gps& gps_msg);
  void ImuCallback(const Imu& imu_msg);

 private:
  // IMU数据缓冲区
  std::list<ImuData> imu_list_;
  int imu_list_max_size_ = 50;

  // GPS状态缓冲区
  std::list<GpsStatus> gps_status_list_;
  int gps_status_list_max_size_ = 10;

  // 地图偏移
  double map_offset_[3];  // [x, y, z]
};

GPS回调处理流程:

void RTKLocalization::GpsCallback(const Gps& gps_msg) {
  // ===== 步骤1: 查找匹配的IMU消息 =====
  double gps_timestamp = gps_msg.header().timestamp_sec();
  ImuData imu_msg;
  if (!FindMatchingIMU(gps_timestamp, &imu_msg)) {
    AERROR << "Cannot find matching IMU for GPS at " << gps_timestamp;
    return;
  }

  // ===== 步骤2: IMU时间插值 =====
  // 如果GPS时间不完全匹配IMU时间,进行线性插值
  ImuData interpolated_imu;
  if (NeedInterpolation(gps_timestamp, imu_msg)) {
    ImuData imu1, imu2;
    FindNearestIMU(gps_timestamp, &imu1, &imu2);
    InterpolateIMU(imu1, imu2, gps_timestamp, &interpolated_imu);
  } else {
    interpolated_imu = imu_msg;
  }

  // ===== 步骤3: 组合定位消息 =====
  LocalizationEstimate localization;
  ComposeLocalizationMsg(gps_msg, interpolated_imu, &localization);

  // ===== 步骤4: 发布定位结果 =====
  PublishLocalization(localization);
}

IMU时间插值:

bool InterpolateIMU(const ImuData& imu1, const ImuData& imu2,
                   double timestamp, ImuData* output) {
  // 计算插值权重
  double total_delta = imu2.timestamp - imu1.timestamp;
  if (total_delta <= 0) return false;

  double frac1 = (timestamp - imu1.timestamp) / total_delta;
  double frac2 = 1.0 - frac1;

  // 线性插值 - 加速度
  output->linear_acceleration.x =
      imu1.linear_acceleration.x * frac2 +
      imu2.linear_acceleration.x * frac1;
  output->linear_acceleration.y =
      imu1.linear_acceleration.y * frac2 +
      imu2.linear_acceleration.y * frac1;
  output->linear_acceleration.z =
      imu1.linear_acceleration.z * frac2 +
      imu2.linear_acceleration.z * frac1;

  // 线性插值 - 角速度
  output->angular_velocity.x =
      imu1.angular_velocity.x * frac2 +
      imu2.angular_velocity.x * frac1;
  output->angular_velocity.y =
      imu1.angular_velocity.y * frac2 +
      imu2.angular_velocity.y * frac1;
  output->angular_velocity.z =
      imu1.angular_velocity.z * frac2 +
      imu2.angular_velocity.z * frac1;

  output->timestamp = timestamp;
  return true;
}

5.3 坐标转换

世界坐标系 → 地图坐标系:

void ComposeLocalizationMsg(const Gps& gps_msg,
                           const ImuData& imu_msg,
                           LocalizationEstimate* localization) {
  // 位置转换
  localization->mutable_pose()->mutable_position()->set_x(
      gps_msg.position().x() - map_offset_[0]);
  localization->mutable_pose()->mutable_position()->set_y(
      gps_msg.position().y() - map_offset_[1]);
  localization->mutable_pose()->mutable_position()->set_z(
      gps_msg.position().z() - map_offset_[2]);

  // 姿态(四元数)
  localization->mutable_pose()->mutable_orientation()->CopyFrom(
      gps_msg.orientation());

  // 航向角计算
  double heading = QuaternionToHeading(
      gps_msg.orientation().qw(),
      gps_msg.orientation().qx(),
      gps_msg.orientation().qy(),
      gps_msg.orientation().qz());
  localization->mutable_pose()->set_heading(heading);

  // 速度(地图坐标系)
  localization->mutable_pose()->mutable_linear_velocity()->CopyFrom(
      gps_msg.linear_velocity());

  // 加速度转换(车体坐标系 → 地图坐标系)
  Vector3d acc_vehicle(
      imu_msg.linear_acceleration.x,
      imu_msg.linear_acceleration.y,
      imu_msg.linear_acceleration.z);
  Vector3d acc_map = QuaternionRotate(
      gps_msg.orientation(), acc_vehicle);
  localization->mutable_pose()->mutable_linear_acceleration()->set_x(
      acc_map.x());
  localization->mutable_pose()->mutable_linear_acceleration()->set_y(
      acc_map.y());
  localization->mutable_pose()->mutable_linear_acceleration()->set_z(
      acc_map.z());

  // 角速度转换(车体坐标系 → 地图坐标系)
  Vector3d omega_vehicle(
      imu_msg.angular_velocity.x,
      imu_msg.angular_velocity.y,
      imu_msg.angular_velocity.z);
  Vector3d omega_map = QuaternionRotate(
      gps_msg.orientation(), omega_vehicle);
  localization->mutable_pose()->mutable_angular_velocity()->set_x(
      omega_map.x());
  localization->mutable_pose()->mutable_angular_velocity()->set_y(
      omega_map.y());
  localization->mutable_pose()->mutable_angular_velocity()->set_z(
      omega_map.z());
}

5.4 配置参数

配置文件: modules/localization/conf/rtk_localization.pb.txt

localization_topic: "/apollo/localization/pose"
imu_topic: "/apollo/sensor/gnss/imu"
gps_topic: "/apollo/sensor/gnss/best_pose"

imu_list_max_size: 50
gps_imu_time_diff_threshold: 0.02   # 20ms
gps_time_delay_tolerance: 1.0       # 1秒

map_offset_x: 0.0
map_offset_y: 0.0
map_offset_z: 0.0

enable_tf2_transform: false

6. MSF多传感器融合定位

6.1 MSF系统概述

MSF (Multi-Sensor Fusion): 融合GPS、IMU、LiDAR实现高精度定位

核心组件: modules/localization/msf/local_integ/localization_integ.h

6.2 LocalizationInteg接口

代码位置: localization_integ.h 行1-90

class LocalizationInteg {
 public:
  // 初始化
  common::Status Init(const LocalizationIntegParam &params);

  // ===== 点云处理 =====
  void PcdProcess(const drivers::PointCloud &message);

  // ===== IMU处理 =====
  // FLU坐标系: Front-Left-Up (前左上)
  void RawImuProcessFlu(const drivers::gnss::Imu &imu_msg);
  // RFU坐标系: Right-Front-Up (右前上)
  void RawImuProcessRfu(const drivers::gnss::Imu &imu_msg);

  // ===== GNSS处理 =====
  // 原始观测值
  void RawObservationProcess(
      const drivers::gnss::EpochObservation &raw_obs_msg);
  // 星历数据
  void RawEphemerisProcess(
      const drivers::gnss::GnssEphemeris &gnss_orbit_msg);
  // GNSS最佳位姿
  void GnssBestPoseProcess(
      const drivers::gnss::GnssBestPose &bestgnsspos_msg);
  // GNSS航向
  void GnssHeadingProcess(
      const drivers::gnss::Heading &gnss_heading_msg);

  // ===== 获取结果 =====
  const LocalizationResult &GetLastestLidarLocalization() const;
  const LocalizationResult &GetLastestIntegLocalization() const;
  const LocalizationResult &GetLastestGnssLocalization() const;
};

6.3 四种工作模式

配置参数 (localization_msf/integ_sins_pva.h):

// GNSS模式
gnss_mode: 01
  0: 使用BestGNSS (RTK定位结果)
  1: 使用Local-GNSS (原始观测值计算定位)

// GNSS初始化模式
gnss_only_init: truefalse
  false: GNSS持续参与融合
  true: GNSS仅用于初始化SINS对齐

四种模式:

模式1: 三系统 (BestGNSS + LiDAR + SINS)
  gnss_mode=0, gnss_only_init=false
  - GNSS持续提供位置更新
  - LiDAR提供位姿校正
  - SINS积分平滑

模式2: 三系统 (Local-GNSS + LiDAR + SINS)
  gnss_mode=1, gnss_only_init=false
  - 使用原始GNSS观测值
  - 更高的定位精度
  - 计算复杂度更高

模式3: 二系统 (BestGNSS + LiDAR + SINS)
  gnss_mode=0, gnss_only_init=true
  - GNSS仅初始化
  - 主要依赖LiDAR和SINS
  - 适合GPS信号不稳定场景

模式4: 二系统 (Local-GNSS + LiDAR + SINS)
  gnss_mode=1, gnss_only_init=true
  - 原始GNSS观测仅初始化
  - 最高精度但计算量大

6.4 LiDAR定位

LocalizationLidar (localization_lidar.h):

class LocalizationLidar {
 public:
  // 三种定位模式
  enum LocalizationMode {
    INTENSITY,  // 反射强度匹配
    ALTITUDE,   // 高程匹配
    FUSION      // 融合模式(推荐)
  };

  // 初始化地图
  bool Init(const string& map_path,
           unsigned int search_range_x,
           unsigned int search_range_y,
           int zone_id);

  // 更新定位
  int Update(unsigned int frame_idx,
            const Affine3d& pose,
            const Vector3d velocity,
            const LidarFrame& lidar_frame,
            bool use_avx = false);

  // 获取结果
  void GetResult(Affine3d* location,
                Matrix3d* covariance,
                double* location_score);
};

金字塔地图 (pyramid_map/pyramid_map.h):

class PyramidMap {
  // 地图节点
  struct MapNode {
    float intensity_avg;    // 反射强度均值
    float intensity_var;    // 反射强度方差
    float altitude;         // 高程
    int count;             // 点数统计
  };

  // 分辨率
  float resolution_ = 0.125;  // 默认0.125米

  // 地图数据
  std::map<MapNodeIndex, MapNode> nodes_;
};

7. NDT点云配准算法

7.1 NDT算法原理

NDT (Normal Distributions Transform): 正态分布变换

核心思想: 将点云表示为正态分布的集合

算法步骤:

1. 体素化
   └─ 将目标点云划分为3D体素网格

2. 统计建模
   └─ 每个体素内的点拟合正态分布
      ├─ 均值: μ = (1/N) Σ p_i
      └─ 协方差: Σ = (1/N) Σ (p_i - μ)(p_i - μ)ᵀ

3. 概率匹配
   └─ 源点云变换后,计算每个点落在目标分布的概率

4. 优化
   └─ 最大化总概率 = 最小化负对数似然
      ├─ 使用牛顿法迭代
      └─ 计算梯度和Hessian矩阵

5. 收敛检查
   └─ 变换增量 < 阈值

7.2 NDT实现

核心类: NormalDistributionsTransform

位置: modules/localization/ndt/ndt_locator/ndt_solver.h

template <typename PointSource, typename PointTarget>
class NormalDistributionsTransform {
 public:
  // ===== 参数设置 =====
  void SetResolution(float resolution) {
    resolution_ = resolution;
  }

  void SetStepSize(double step_size) {
    step_size_ = step_size;
  }

  void SetMaximumIterations(int max_iterations) {
    max_iterations_ = max_iterations;
  }

  void SetTransformationEpsilon(double epsilon) {
    transformation_epsilon_ = epsilon;
  }

  void SetOutlierRatio(double ratio) {
    outlier_ratio_ = ratio;
  }

  // ===== 输入设置 =====
  void SetInputTarget(const PointCloudTargetPtr& cloud);
  void SetInputSource(const PointCloudSourcePtr& cloud);

  // ===== 配准执行 =====
  void Align(PointCloudSourcePtr output,
            const Matrix4f& guess);

  // ===== 结果获取 =====
  Matrix4f GetFinalTransformation() const {
    return final_transformation_;
  }

  Matrix3d GetLocationCovariance() const {
    return location_covariance_;
  }

  double GetFitnessScore() const {
    return fitness_score_;
  }

 private:
  // 核心计算方法
  void ComputeTransformation(PointCloudSourcePtr output,
                            const Matrix4f& guess);

  double ComputeDerivatives(Matrix<6,1>* score_gradient,
                           Matrix<6,6>* hessian,
                           PointCloudSourcePtr trans_cloud,
                           Matrix<6,1>* p);

  void ComputeStepLengthMt(const Matrix<6,1>& x,
                          Matrix<6,1>* delta_x,
                          double step_init,
                          double step_max,
                          double step_min,
                          ...);

  void UpdateDerivatives(Matrix<6,1>* score_gradient,
                        Matrix<6,6>* hessian,
                        const Vector3d& x_trans,
                        const Matrix3d& cov_inv);

  // 成员变量
  float resolution_;              // 体素分辨率
  double step_size_;              // 牛顿法步长
  double outlier_ratio_;          // 离群点比例
  int max_iterations_;            // 最大迭代次数
  double transformation_epsilon_; // 收敛阈值

  Matrix4f final_transformation_; // 最终变换
  Matrix3d location_covariance_;  // 位置协方差
  double fitness_score_;          // 配准分数
};

7.3 ComputeTransformation方法

牛顿法优化:

void ComputeTransformation(PointCloudSourcePtr output,
                          const Matrix4f& guess) {
  // 初始化变换
  transformation_ = guess;
  Matrix<6,1> x;  // 6DOF位姿参数 [x, y, z, roll, pitch, yaw]
  TransformationToVector(transformation_, &x);

  // 迭代优化
  for (int iter = 0; iter < max_iterations_; ++iter) {
    // 1. 应用当前变换
    PointCloudSourcePtr trans_cloud(new PointCloudSource);
    TransformPointCloud(*input_source_, *trans_cloud, transformation_);

    // 2. 计算梯度和Hessian矩阵
    Matrix<6,1> score_gradient;
    Matrix<6,6> hessian;
    double score = ComputeDerivatives(
        &score_gradient, &hessian, trans_cloud, &x);

    // 3. 检查是否需要继续
    if (score_gradient.norm() < 1e-4) {
      AINFO << "Gradient too small, converged.";
      break;
    }

    // 4. 牛顿法更新
    Matrix<6,1> delta_x = -hessian.inverse() * score_gradient;

    // 5. 线搜索优化步长
    double step_init = 1.0;
    double step_max = 1.0;
    double step_min = 0.0;
    ComputeStepLengthMt(x, &delta_x, step_init,
                       step_max, step_min, ...);

    // 6. 更新参数
    x = x + step_size_ * delta_x;
    VectorToTransformation(x, &transformation_);

    // 7. 检查收敛
    if (delta_x.norm() < transformation_epsilon_) {
      AINFO << "Transformation change too small, converged at iter "
            << iter;
      break;
    }

    ADEBUG << "Iteration " << iter << ", score: " << score
           << ", delta_x norm: " << delta_x.norm();
  }

  // 保存最终结果
  final_transformation_ = transformation_;
  TransformPointCloud(*input_source_, *output, final_transformation_);

  // 计算配准分数
  fitness_score_ = ComputeFitnessScore(output);
}

7.4 ComputeDerivatives方法

梯度和Hessian计算:

double ComputeDerivatives(Matrix<6,1>* score_gradient,
                         Matrix<6,6>* hessian,
                         PointCloudSourcePtr trans_cloud,
                         Matrix<6,1>* p) {
  score_gradient->setZero();
  hessian->setZero();
  double score = 0.0;

  // 遍历所有源点
  for (size_t i = 0; i < trans_cloud->size(); ++i) {
    const PointSource& point = trans_cloud->points[i];

    // 查找对应的目标体素
    Vector3d x(point.x, point.y, point.z);
    Leaf* cell = target_cells_.GetLeaf(x);
    if (!cell || cell->GetPointCount() < min_points_per_voxel_) {
      continue;  // 跳过无效体素
    }

    // 获取正态分布参数
    Vector3d mean = cell->GetMean();
    Matrix3d cov = cell->GetCovariance();
    Matrix3d cov_inv = cov.inverse();

    // 计算点到分布的距离
    Vector3d diff = x - mean;
    double exponent = -0.5 * diff.transpose() * cov_inv * diff;
    double prob = exp(exponent);

    // 更新分数(负对数似然)
    score -= prob;

    // 计算雅可比矩阵 J = ∂x/∂p
    Matrix<3,6> jacobian;
    ComputePointJacobian(x, p, &jacobian);

    // 更新梯度: g = J^T * Σ^-1 * (x - μ) * prob
    Vector3d grad_xyz = cov_inv * diff * prob;
    *score_gradient += jacobian.transpose() * grad_xyz;

    // 更新Hessian: H = J^T * Σ^-1 * J * prob
    *hessian += jacobian.transpose() * cov_inv * jacobian * prob;
  }

  return score;
}

概率密度函数:

P(x|μ,Σ) = exp(-0.5 * (x-μ)ᵀΣ⁻¹(x-μ)) / √((2π)³|Σ|)

负对数似然:
-log P(x) = 0.5 * (x-μ)ᵀΣ⁻¹(x-μ) + const

最小化负对数似然 = 最大化概率

7.5 NDT参数配置

典型参数:

// 体素分辨率
resolution: 1.0  // 米

// 牛顿法步长
step_size: 0.1

// 最大迭代次数
max_iterations: 10

// 收敛阈值
transformation_epsilon: 0.01

// 离群点比例
outlier_ratio: 0.55  // 55%的点可能是离群点

8. 卡尔曼滤波实现

8.1 卡尔曼滤波基础

位置: modules/common/math/kalman_filter.h

模板类定义 (行49-252):

template <typename T, unsigned int XN, unsigned int ZN, unsigned int UN>
class KalmanFilter {
 public:
  // ===== 构造函数 =====
  KalmanFilter() {
    F_.setIdentity();  // 状态转移矩阵
    Q_.setZero();      // 过程噪声协方差
    H_.setIdentity();  // 观测矩阵
    R_.setZero();      // 观测噪声协方差
    B_.setZero();      // 控制矩阵

    x_.setZero();      // 状态向量
    P_.setZero();      // 状态协方差
  }

  // ===== 初始化 =====
  void SetStateEstimate(const Eigen::Matrix<T, XN, 1> &x,
                        const Eigen::Matrix<T, XN, XN> &P) {
    x_ = x;
    P_ = P;
    is_initialized_ = true;
  }

  // ===== 预测步骤 =====
  void Predict(const Eigen::Matrix<T, UN, 1> &u =
               Eigen::Matrix<T, UN, 1>::Zero());

  // ===== 更新步骤 =====
  void Correct(const Eigen::Matrix<T, ZN, 1> &z);

  // ===== 获取结果 =====
  Eigen::Matrix<T, XN, 1> GetStateEstimate() const { return x_; }
  Eigen::Matrix<T, XN, XN> GetStateCovariance() const { return P_; }

 private:
  // 状态
  Eigen::Matrix<T, XN, 1> x_;      // 状态向量
  Eigen::Matrix<T, XN, XN> P_;     // 状态协方差

  // 系统模型
  Eigen::Matrix<T, XN, XN> F_;     // 状态转移矩阵
  Eigen::Matrix<T, XN, XN> Q_;     // 过程噪声协方差
  Eigen::Matrix<T, XN, UN> B_;     // 控制矩阵

  // 观测模型
  Eigen::Matrix<T, ZN, XN> H_;     // 观测矩阵
  Eigen::Matrix<T, ZN, ZN> R_;     // 观测噪声协方差

  // 中间变量
  Eigen::Matrix<T, ZN, 1> y_;      // 新息
  Eigen::Matrix<T, ZN, ZN> S_;     // 新息协方差
  Eigen::Matrix<T, XN, ZN> K_;     // 卡尔曼增益

  bool is_initialized_ = false;
};

8.2 预测步骤

代码位置: kalman_filter.h 行255-262

template <typename T, unsigned int XN, unsigned int ZN, unsigned int UN>
inline void KalmanFilter<T, XN, ZN, UN>::Predict(
    const Eigen::Matrix<T, UN, 1> &u) {
  ACHECK(is_initialized_);

  // 状态预测
  x_ = F_ * x_ + B_ * u;

  // 协方差预测
  P_ = F_ * P_ * F_.transpose() + Q_;
}

预测方程:

x̂⁻ = F × x̂⁺ + B × u
P⁻ = F × P⁺ × Fᵀ + Q

其中:
  x̂⁻: 先验状态估计
  x̂⁺: 后验状态估计
  P⁻: 先验协方差
  P⁺: 后验协方差
  F: 状态转移矩阵
  B: 控制矩阵
  u: 控制输入
  Q: 过程噪声协方差

8.3 更新步骤

代码位置: kalman_filter.h 行264-279

template <typename T, unsigned int XN, unsigned int ZN, unsigned int UN>
inline void KalmanFilter<T, XN, ZN, UN>::Correct(
    const Eigen::Matrix<T, ZN, 1> &z) {
  ACHECK(is_initialized_);

  // 新息(innovation)
  y_ = z - H_ * x_;

  // 新息协方差
  S_ = static_cast<Eigen::Matrix<T, ZN, ZN>>(
      H_ * P_ * H_.transpose() + R_);

  // 卡尔曼增益
  K_ = static_cast<Eigen::Matrix<T, XN, ZN>>(
      P_ * H_.transpose() * PseudoInverse<T, ZN>(S_));

  // 状态更新
  x_ = x_ + K_ * y_;

  // 协方差更新
  P_ = static_cast<Eigen::Matrix<T, XN, XN>>(
      (Eigen::Matrix<T, XN, XN>::Identity() - K_ * H_) * P_);
}

更新方程:

y = z - H × x̂⁻                              (新息)
S = H × P⁻ × Hᵀ + R                          (新息协方差)
K = P⁻ × Hᵀ × S⁻¹                            (卡尔曼增益)
x̂⁺ = x̂⁻ + K × y                             (状态更新)
P⁺ = (I - K × H) × P⁻                        (协方差更新)

其中:
  z: 观测值
  H: 观测矩阵
  R: 观测噪声协方差
  K: 卡尔曼增益(核心!)
  I: 单位矩阵
8.3.1 扩展卡尔曼滤波(EKF)深度解析

扩展卡尔曼滤波 (Extended Kalman Filter, EKF)

标准卡尔曼滤波要求系统是线性的,但实际定位系统往往是非线性的。EKF通过一阶泰勒展开(线性化)将非线性系统近似为线性系统。

非线性系统模型:

状态方程: x_k = f(x_{k-1}, u_{k-1}) + w_{k-1}
观测方程: z_k = h(x_k) + v_k

其中:
  f(): 非线性状态转移函数
  h(): 非线性观测函数
  w_k ~ N(0, Q): 过程噪声
  v_k ~ N(0, R): 观测噪声

EKF线性化

对非线性函数在当前估计点进行一阶泰勒展开:

f(x) ≈ f(x̂) + F × (x - x̂)
h(x) ≈ h(x̂) + H × (x - x̂)

其中雅可比矩阵:
F = ∂f/∂x |_{x=x̂}  (状态转移雅可比)
H = ∂h/∂x |_{x=x̂}  (观测雅可比)

Apollo EKF实现 (modules/localization/msf/local_integ/sins_struct.h):

// 15维状态向量
struct SinsPva {
  // 位置 (3维)
  Eigen::Vector3d pos;       // [latitude, longitude, altitude]

  // 速度 (3维)
  Eigen::Vector3d vel;       // [v_north, v_east, v_down]

  // 姿态四元数 (4维)
  Eigen::Quaterniond qbn;    // 车体系到导航系的旋转

  // IMU偏差 (6维)
  Eigen::Vector3d gyro_bias;  // 陀螺仪偏差
  Eigen::Vector3d acc_bias;   // 加速度计偏差

  // 总维度: 3 + 3 + 4 + 3 + 3 = 16维
  // 实际使用15维 (四元数3维误差 + 其他12维 = 15维)
};

// 状态误差向量 (15维)
// δx = [δp, δv, δθ, δba, δbg]'
// δp: 位置误差 (3维)
// δv: 速度误差 (3维)
// δθ: 姿态误差 (3维,四元数误差用旋转向量表示)
// δba: 加速度计偏差 (3维)
// δbg: 陀螺仪偏差 (3维)

EKF预测步骤

// modules/localization/msf/local_integ/localization_integ_impl.cc

void LocalizationIntegImpl::ImuMeasureUpdate(const ImuData& imu) {
  // Step 1: 状态传播 (非线性状态方程)
  SinsPva sins_pva_new;

  // 1.1 姿态更新 (四元数微分方程)
  Eigen::Vector3d omega_b = imu.angular_velocity - sins_pva_.gyro_bias;
  Eigen::Quaterniond dq = Eigen::Quaterniond(
      1.0, 0.5*omega_b.x()*dt, 0.5*omega_b.y()*dt, 0.5*omega_b.z()*dt);
  sins_pva_new.qbn = sins_pva_.qbn * dq;
  sins_pva_new.qbn.normalize();

  // 1.2 速度更新
  Eigen::Vector3d f_b = imu.linear_acceleration - sins_pva_.acc_bias;
  Eigen::Vector3d f_n = sins_pva_.qbn.toRotationMatrix() * f_b;  // 转到导航系
  Eigen::Vector3d g_n(0, 0, -9.80665);  // 重力加速度
  sins_pva_new.vel = sins_pva_.vel + (f_n + g_n) * dt;

  // 1.3 位置更新
  sins_pva_new.pos = sins_pva_.pos + sins_pva_.vel * dt +
                     0.5 * (f_n + g_n) * dt * dt;

  // 1.4 IMU偏差保持不变 (一阶马尔科夫过程)
  sins_pva_new.gyro_bias = sins_pva_.gyro_bias;
  sins_pva_new.acc_bias = sins_pva_.acc_bias;

  // Step 2: 误差协方差传播 (线性化)
  // P⁻ = F × P⁺ × Fᵀ + Q

  // 2.1 计算状态转移雅可比矩阵 F (15×15)
  Eigen::Matrix<double, 15, 15> F = Eigen::Matrix<double, 15, 15>::Zero();

  // F的分块结构:
  // ┌─────────────────────────────────────┐
  // │ I   I*dt    0      0        0       │  位置行
  // │ 0   I       -C*[f_b×]  0    -C      │  速度行
  // │ 0   0       I      0        -I      │  姿态行
  // │ 0   0       0      I        0       │  加速度计偏差行
  // │ 0   0       0      0        I       │  陀螺仪偏差行
  // └─────────────────────────────────────┘

  // 位置行
  F.block<3, 3>(0, 0) = Eigen::Matrix3d::Identity();
  F.block<3, 3>(0, 3) = Eigen::Matrix3d::Identity() * dt;

  // 速度行
  F.block<3, 3>(3, 3) = Eigen::Matrix3d::Identity();
  Eigen::Matrix3d C_bn = sins_pva_.qbn.toRotationMatrix();
  F.block<3, 3>(3, 6) = -C_bn * SkewSymmetric(f_b);
  F.block<3, 3>(3, 12) = -C_bn * dt;

  // 姿态行
  F.block<3, 3>(6, 6) = Eigen::Matrix3d::Identity();
  F.block<3, 3>(6, 9) = -Eigen::Matrix3d::Identity() * dt;

  // IMU偏差行
  F.block<3, 3>(9, 9) = Eigen::Matrix3d::Identity();
  F.block<3, 3>(12, 12) = Eigen::Matrix3d::Identity();

  // 2.2 离散化 (一阶近似)
  Eigen::Matrix<double, 15, 15> Phi = Eigen::Matrix<double, 15, 15>::Identity() + F * dt;

  // 2.3 过程噪声协方差 Q (15×15)
  Eigen::Matrix<double, 15, 15> Q = Eigen::Matrix<double, 15, 15>::Zero();

  // 加速度计噪声
  double sigma_acc = 0.01;  // m/s²
  Q.block<3, 3>(3, 3) = Eigen::Matrix3d::Identity() * sigma_acc * sigma_acc * dt * dt;

  // 陀螺仪噪声
  double sigma_gyro = 0.001;  // rad/s
  Q.block<3, 3>(6, 6) = Eigen::Matrix3d::Identity() * sigma_gyro * sigma_gyro * dt * dt;

  // 加速度计偏差随机游走
  double sigma_acc_bias = 0.0001;
  Q.block<3, 3>(9, 9) = Eigen::Matrix3d::Identity() * sigma_acc_bias * sigma_acc_bias * dt;

  // 陀螺仪偏差随机游走
  double sigma_gyro_bias = 0.00001;
  Q.block<3, 3>(12, 12) = Eigen::Matrix3d::Identity() * sigma_gyro_bias * sigma_gyro_bias * dt;

  // 2.4 协方差更新
  P_ = Phi * P_ * Phi.transpose() + Q;

  // 更新状态
  sins_pva_ = sins_pva_new;
}

辅助函数:

// 反对称矩阵 (用于叉乘)
Eigen::Matrix3d SkewSymmetric(const Eigen::Vector3d& v) {
  Eigen::Matrix3d m;
  m <<     0, -v.z(),  v.y(),
       v.z(),      0, -v.x(),
      -v.y(),  v.x(),      0;
  return m;
}

// 示例:
// v = [vx, vy, vz]'
// v × w = SkewSymmetric(v) * w

EKF更新步骤 (GPS观测)

void LocalizationIntegImpl::GnssMeasureUpdate(const GnssBestPose& gnss) {
  // Step 1: 观测向量 z (3维位置)
  Eigen::Vector3d z_gnss(gnss.latitude, gnss.longitude, gnss.altitude);

  // Step 2: 计算观测预测值 (从当前状态预测观测)
  Eigen::Vector3d z_pred = sins_pva_.pos;  // h(x) = x.pos

  // Step 3: 新息 y
  Eigen::Vector3d y = z_gnss - z_pred;

  // Step 4: 观测雅可比矩阵 H (3×15)
  // h(x) = x.pos, 所以 H = [I_3×3, 0_3×12]
  Eigen::Matrix<double, 3, 15> H = Eigen::Matrix<double, 3, 15>::Zero();
  H.block<3, 3>(0, 0) = Eigen::Matrix3d::Identity();

  // Step 5: 观测噪声协方差 R (3×3)
  Eigen::Matrix3d R = Eigen::Matrix3d::Zero();
  R(0, 0) = gnss.latitude_std * gnss.latitude_std;
  R(1, 1) = gnss.longitude_std * gnss.longitude_std;
  R(2, 2) = gnss.altitude_std * gnss.altitude_std;

  // Step 6: 新息协方差 S
  Eigen::Matrix3d S = H * P_ * H.transpose() + R;

  // Step 7: 卡尔曼增益 K (15×3)
  Eigen::Matrix<double, 15, 3> K = P_ * H.transpose() * S.inverse();

  // Step 8: 状态误差更新
  Eigen::Matrix<double, 15, 1> delta_x = K * y;

  // Step 9: 状态修正
  // 位置修正
  sins_pva_.pos += delta_x.segment<3>(0);

  // 速度修正
  sins_pva_.vel += delta_x.segment<3>(3);

  // 姿态修正 (小角度近似)
  Eigen::Vector3d delta_theta = delta_x.segment<3>(6);
  Eigen::Quaterniond dq_corr(1.0, 0.5*delta_theta.x(),
                              0.5*delta_theta.y(), 0.5*delta_theta.z());
  sins_pva_.qbn = sins_pva_.qbn * dq_corr;
  sins_pva_.qbn.normalize();

  // IMU偏差修正
  sins_pva_.acc_bias += delta_x.segment<3>(9);
  sins_pva_.gyro_bias += delta_x.segment<3>(12);

  // Step 10: 协方差更新
  Eigen::Matrix<double, 15, 15> I_KH =
      Eigen::Matrix<double, 15, 15>::Identity() - K * H;
  P_ = I_KH * P_ * I_KH.transpose() + K * R * K.transpose();
  // 使用Joseph形式保证数值稳定性
}

卡尔曼增益的物理意义

K = P⁻ × Hᵀ × (H × P⁻ × Hᵀ + R)⁻¹

分析:
1. 如果R很大(观测不可靠):
   K → 0, 更相信预测, 忽略观测

2. 如果P⁻很小(预测很可靠):
   K → 0, 更相信预测

3. 如果P⁻很大(预测不可靠):
   K → Hᵀ × (H × H' + R)⁻¹, 更相信观测

4. 最优情况:
   K自动权衡预测和观测的可靠性

完整使用示例

// 定位系统中的EKF应用
class LocalizationEKF {
 public:
  void Init(const Eigen::Vector3d& init_pos,
            const Eigen::Vector3d& init_vel,
            const Eigen::Quaterniond& init_quat) {
    // 初始化状态
    sins_pva_.pos = init_pos;
    sins_pva_.vel = init_vel;
    sins_pva_.qbn = init_quat;
    sins_pva_.acc_bias.setZero();
    sins_pva_.gyro_bias.setZero();

    // 初始化协方差
    P_.setIdentity();
    P_.block<3, 3>(0, 0) *= 1.0;      // 位置初始不确定性 1m²
    P_.block<3, 3>(3, 3) *= 0.1;      // 速度初始不确定性 0.1(m/s)²
    P_.block<3, 3>(6, 6) *= 0.01;     // 姿态初始不确定性 0.01rad²
    P_.block<3, 3>(9, 9) *= 0.001;    // 加速度计偏差
    P_.block<3, 3>(12, 12) *= 0.0001; // 陀螺仪偏差
  }

  void Process(const ImuData& imu,
               const std::optional<GnssBestPose>& gnss) {
    // IMU预测 (100Hz)
    ImuMeasureUpdate(imu);

    // GNSS更新 (1Hz)
    if (gnss.has_value()) {
      GnssMeasureUpdate(gnss.value());
    }
  }

  LocalizationEstimate GetEstimate() const {
    LocalizationEstimate est;
    est.pose.position = sins_pva_.pos;
    est.pose.linear_velocity = sins_pva_.vel;
    est.pose.orientation = sins_pva_.qbn;
    est.pose.linear_acceleration_vrf = sins_pva_.acc_bias;
    est.pose.angular_velocity_vrf = sins_pva_.gyro_bias;

    // 不确定性
    est.uncertainty.position_std_dev.x = std::sqrt(P_(0, 0));
    est.uncertainty.position_std_dev.y = std::sqrt(P_(1, 1));
    est.uncertainty.position_std_dev.z = std::sqrt(P_(2, 2));

    return est;
  }

 private:
  SinsPva sins_pva_;                          // 状态
  Eigen::Matrix<double, 15, 15> P_;           // 协方差
};

// 使用
LocalizationEKF ekf;
ekf.Init(init_pos, init_vel, init_quat);

while (true) {
  ImuData imu = ReadImuData();  // 100Hz
  std::optional<GnssBestPose> gnss = ReadGnssData();  // 1Hz

  ekf.Process(imu, gnss);

  LocalizationEstimate est = ekf.GetEstimate();
  PublishLocalization(est);
}

性能指标

典型精度 (在良好GNSS条件下):
- 位置精度: 0.05-0.10米 (RMS)
- 速度精度: 0.02-0.05m/s (RMS)
- 姿态精度: 0.1-0.2度 (RMS)

计算性能:
- 预测步骤: 0.1-0.2ms
- 更新步骤: 0.5-1.0ms
- 总延迟: < 2ms

优势:
- 高频IMU预测保证平滑性
- 低频GNSS更新消除累积误差
- 协方差自动权衡传感器可靠性

S = H × P⁻ × Hᵀ + R (新息协方差)
K = P⁻ × Hᵀ × S⁻¹ (卡尔曼增益)
x̂⁺ = x̂⁻ + K × y (状态更新)
P⁺ = (I - K × H) × P⁻ (协方差更新)

其中:
z: 观测值
H: 观测矩阵
R: 观测噪声协方差
K: 卡尔曼增益
y: 新息(实际观测与预测观测的差)


### 8.4 在预测模块中的应用

**Obstacle类中的卡尔曼滤波器**:

```cpp
class Obstacle {
 private:
  // 卡尔曼滤波器
  // 状态维度: 6 [x, y, vx, vy, ax, ay]
  // 观测维度: 2 [x, y]
  // 控制维度: 0 (无控制输入)
  KalmanFilter<double, 6, 2, 0> kf_;
};

状态向量定义:

x = [x, y, vx, vy, ax, ay]ᵀ

其中:
  x, y: 位置
  vx, vy: 速度
  ax, ay: 加速度

状态转移矩阵F (常速度模型):

F = [1  0  Δt  0   0.5Δt²  0    ]
    [0  1  0   Δt  0       0.5Δt²]
    [0  0  1   0   Δt      0    ]
    [0  0  0   1   0       Δt   ]
    [0  0  0   0   1       0    ]
    [0  0  0   0   0       1    ]

其中 Δt 为时间间隔(0.1秒)

观测矩阵H:

H = [1  0  0  0  0  0]
    [0  1  0  0  0  0]

只观测位置,不直接观测速度和加速度

参数配置:

// prediction_gflags.h
FLAGS_enable_kf_tracking: true
FLAGS_q_var: 0.01  // 过程噪声方差
FLAGS_r_var: 0.1   // 观测噪声方差
FLAGS_p_var: 0.1   // 初始协方差

9. 总结

9.1 预测模块核心要点

  1. 四层架构: Container → Scenario → Evaluator → Predictor
  2. 9种评估器: 覆盖各种场景和障碍物类型
  3. LSTM预测: 结合语义地图和历史轨迹,预测3秒轨迹
  4. VectorNet: 向量化地图表示,高效编码环境信息
  5. 交互预测: 考虑主车规划轨迹,实现联合预测规划

9.2 定位模块核心要点

  1. 三种方法: RTK、MSF、NDT,适应不同场景
  2. RTK定位: GPS+IMU,厘米级精度,适合开阔场景
  3. MSF融合: GPS+IMU+LiDAR,全场景高精度
  4. NDT配准: 正态分布变换,GPS失效时的备选方案
  5. 卡尔曼滤波: 最优状态估计,融合多源观测

9.3 关键算法总结

LSTM轨迹预测:

输入: 224×224×3语义地图 + 20帧历史轨迹
网络: CNN特征提取 + LSTM时序建模
输出: 30个未来轨迹点(3秒,0.1秒间隔)
性能: 10-20ms (GPU)

NDT点云配准:

原理: 正态分布变换
算法: 牛顿法优化
参数: 分辨率1.0m, 最大迭代10次
性能: 50-100ms
精度: 分米级

卡尔曼滤波:

状态: 6维 [x, y, vx, vy, ax, ay]
观测: 2维 [x, y]
模型: 常速度模型
应用: 轨迹平滑、速度估计

9.4 性能指标

预测模块:

预测时长: 8秒
预测频率: 10Hz
时间分辨率: 0.1秒
推理时间: 10-50ms (取决于模型)

定位模块:

RTK精度: < 0.05m (横向)
MSF精度: < 0.1m (横向)
NDT精度: < 0.2m (横向)
更新频率: 100Hz (IMU主频)
定位延迟: < 50ms

下一部分预告:第三部分: 规划控制 - 场景化规划、Piecewise Jerk、LQR/PID/MPC

参考资料与引用

官方资源

  1. Apollo 官方 GitHub 仓库
    https://github.com/ApolloAuto/apollo

    • Apollo 开源项目主仓库,包含完整源代码
  2. Apollo 官方文档
    https://apollo.baidu.com/docs

    • 官方技术文档和开发指南
  3. Apollo 开发者社区
    https://apollo.baidu.com/community

    • 官方开发者论坛和技术交流平台

技术规范与标准

  1. ISO 26262 - 道路车辆功能安全标准
    https://www.iso.org/standard/68383.html

  2. ISO 21448 (SOTIF) - 预期功能安全标准
    https://www.iso.org/standard/77490.html

学术论文与技术资源

  1. CenterPoint: Center-based 3D Object Detection and Tracking
    Yin, T., Zhou, X., & Krähenbühl, P. (2021)
    https://arxiv.org/abs/2006.11275

  2. BEVFormer: Learning Bird’s-Eye-View Representation from Multi-Camera Images
    Li, Z., et al. (2022)
    https://arxiv.org/abs/2203.17270

  3. OpenDRIVE 地图标准
    https://www.asam.net/standards/detail/opendrive/

开源工具与库

  1. Bazel 构建系统
    https://bazel.build/

  2. Fast-DDS (eProsima)
    https://www.eprosima.com/index.php/products-all/eprosima-fast-dds

  3. PROJ 坐标转换库
    https://proj.org/

  4. TensorRT 开发指南
    https://docs.nvidia.com/deeplearning/tensorrt/

  5. PCL 点云库文档
    https://pointclouds.org/

  6. IPOPT 优化求解器
    https://coin-or.github.io/Ipopt/

说明

本文档内容整理自上述官方资料、开源代码以及相关技术文档。所有代码示例和技术细节均基于 Apollo 9.0/10.0 版本。如需获取最新信息,请访问 Apollo 官方网站和 GitHub 仓库。

版权说明
Apollo® 是百度公司的注册商标。本文档为基于开源项目的非官方技术研究文档,仅供学习参考使用。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐