EasyCarla-RL:自动驾驶端到端DQR强化学习+轨迹预测输出与carla仿真复现

EasyCarla-RL DQL Planning 架构设计文档

目录

  1. 训练脚本架构 (train_offline_dql_planning.py)
  2. 测试脚本架构 (run_dql_planning_with_trajectory.py)
  3. 数据集加载
  4. 模型输出
  5. 代码解读
  6. 架构对比

训练脚本架构

整体架构

复制代码
train_offline_dql_planning.py
├── 模型定义
│   ├── PlanningActor (规划型Actor网络)
│   ├── Critic (双Q值网络)
│   └── Diffusion_QL_Planning (DQL + 轨迹预测)
├── 数据加载
│   ├── load_dataset() (从HDF5加载)
│   ├── extract_trajectory_from_obs() (提取轨迹目标)
│   ├── compute_reward_from_obs() (从观测计算奖励)
│   └── DataSampler (数据采样器)
├── 训练配置 (config)
└── 训练主循环
    ├── 更新Critic (TD误差)
    ├── 更新Actor (BC损失 + Q损失 + 轨迹损失)
    ├── 软更新目标网络
    └── 模型保存

核心组件

1. PlanningActor
python 复制代码
class PlanningActor(nn.Module):
    """
    规划型Actor网络
    输入: 状态(307维)
    输出: 动作(3维) + 轨迹(20个点,每个点4维: x, y, yaw, speed)
    """

网络结构:

复制代码
Input: state (307维)
    ↓
Shared Backbone
    - Linear (307 → 512)
    - Mish
    - Linear (512 → 512)
    - Mish
    - Linear (512 → 256)
    - Mish
    ↓
    ├─→ Action Head
    │   - Linear (256 → 128)
    │   - Mish
    │   - Linear (128 → 3)  [Throttle, Steer, Brake]
    │
    └─→ Trajectory Head
        - Linear (256 → 256)
        - Mish
        - Linear (256 → 80)  # 20点 × 4维
        - Reshape: (B, 20, 4)  [x, y, yaw, speed]

关键特点:

  • 共享特征提取器,同时学习动作和轨迹
  • 输出轨迹用于规划辅助
  • 无时间嵌入(与Diffusion_BC不同)
2. Critic
python 复制代码
class Critic(nn.Module):
    """标准双Q网络"""

网络结构:

复制代码
Input: state (307) + action (3) = 310维
    ↓
Q1 Network
    - Linear (310 → 256)
    - Mish
    - Linear (256 → 256)
    - Mish
    - Linear (256 → 256)
    - Mish
    - Linear (256 → 1)
    ↓
Q2 Network (相同结构)
    ↓
Q_min = min(Q1, Q2)  # 防止高估

关键特点:

  • 双Q网络结构,避免值高估
  • 输入为状态+动作的拼接
  • 输出单个Q值
3. Diffusion_QL_Planning
python 复制代码
class Diffusion_QL_Planning(object):
    """支持轨迹预测的Diffusion_QL"""

组件:

组件 类型 作用
actor PlanningActor 预测动作和轨迹
critic Critic 评估动作价值
critic_target ListCritic 目标网络(延迟更新)
actor_optimizer Adam Actor优化器
critic_optimizer Adam Critic优化器

超参数:

参数 默认值 说明
discount 0.99 折扣因子
tau 0.005 软更新系数
eta 1.0 Q损失权重
grad_norm 1.0 梯度裁剪
trajectory_loss_weight 0.5 轨迹损失权重

训练流程

复制代码
1. 从DataSampler采样
   (state, action, next_state, reward, not_done, trajectory_target)

2. 更新Critic (TD误差)
   ├─> current_q1, current_q2 = critic(state, action)
   ├─> next_action, _ = actor(next_state)  # 目标策略
   ├─> target_q = min(critic_target(next_state, next_action))
   ├─> target_q = reward + not_done * discount * target_q
   └─> critic_loss = MSE(Q1, target_q) + MSE(Q2, target_q)

3. 更新Actor (多目标损失)
   ├─> new_action, new_trajectory = actor(state)
   ├─> action_loss = MSE(new_action, action)  # BC损失
   ├─> trajectory_loss = MSE(new_trajectory, trajectory_target)  # 轨迹损失
   ├─> q_loss = -Q1_mean / Q2_abs_mean (交替使用Q1/Q2)  # Q损失
   └─> total_loss = action_loss + eta * q_loss + trajectory_loss_weight * trajectory_loss

4. 软更新目标网络
   target_param = tau * param + (1 - tau) * target_param

损失函数设计:

python 复制代码
# 动作损失(模仿学习)
action_loss = F.mse_loss(new_action, action)

# 轨迹损失(规划目标)
trajectory_loss = F.mse_loss(new_trajectory, trajectory_target)

# Q损失(强化学习,最大化动作价值)
if np.random.uniform() > 0.5:
    q_loss = -q1_new.mean() / q2_new.abs().mean().detach()
else:
    q_loss = -q2_new.mean() / q1_new.abs().mean().detach()

# 总损失
total_loss = action_loss + eta * q_loss + trajectory_loss_weight * trajectory_loss

测试脚本架构

整体架构

复制代码
run_dql_planning_with_trajectory.py
├── 模型定义
│   └── PlanningActor (与训练脚本一致)
├── 辅助函数
│   ├── convert_obs_dict_to_vector() (观测字典转向量)
│   ├── compute_road_curvature() (计算道路曲率)
│   ├── get_adaptive_steer_limit() (自适应转向限制)
│   ├── select_best_trajectory() (最优轨迹选择)
│   └── sample_multiple_trajectories() (多轨迹采样)
├── 环境配置 (carla_params)
└── 主测试流程
    ├── 连接CARLA环境
    ├── 加载模型
    ├── 仿真循环
    │   ├── 获取观测
    │   ├── 模型推理
    │   ├── 自适应转向限制
    │   ├── 多轨迹采样
    │   ├── 最优轨迹选择
    │   ├── 执行动作
    │   └── 可视化
    └── 统计输出

核心组件

1. 自适应转向限制
python 复制代码
def compute_road_curvature(waypoints):
    """计算道路曲率(基于前6个waypoint的航向角变化)"""
    
    yaw_changes = []
    for i in range(1, min(6, len(waypoints))):
        yaw_diff = abs(waypoints[i, 2] - waypoints[i-1, 2])
        if yaw_diff > np.pi:
            yaw_diff = 2 * np.pi - yaw_diff
        yaw_changes.append(yaw_diff)
    
    avg_yaw_change = np.mean(yaw_changes) if yaw_changes else 0
    return min(avg_yaw_change / 0.3, 1.0)  # 归一化到[0, 1]

def get_adaptive_steer_limit(curvature, base_limit=0.3, max_limit=1.0):
    """根据道路曲率自适应调整转向限制"""
    return base_limit + curvature * (max_limit - base_limit)

逻辑:

  • 曲率小时(直道):限制转向角度(base_limit=0.3)
  • 曲率大时(弯道):允许更大转向角度(max_limit=1.0)
2. 多轨迹采样
python 复制代码
def sample_multiple_trajectories(actor, state, device, n_samples=8):
    """
    通过添加噪声生成多条候选轨迹
    
    步骤:
    1. 输入噪声: state + Gaussian noise (0.05)
    2. 模型推理: 获取轨迹
    3. 输出噪声: 轨迹位置 + Gaussian noise (0.1), 航向 + (0.05), 速度 + (0.5)
    """
    
    trajectories = []
    with torch.no_grad():
        for _ in range(n_samples):
            noisy_state = state_tensor + torch.randn_like(state_tensor) * 0.05
            _, trajectory = actor(noisy_state)
            
            traj = trajectory.cpu().numpy().squeeze()
            traj[:, :2] += np.random.randn(20, 2) * 0.1  # 位置噪声
            traj[:, 2] += np.random.randn(20) * 0.05     # 航向噪声
            traj[:, 3] += np.random.randn(20) * 0.5      # 速度噪声
            
            trajectories.append(traj)
    
    return np.array(trajectories)  # (8, 20, 4)
3. 最优轨迹选择
python 复制代码
def select_best_trajectory(trajectories, waypoints, ego_speed):
    """
    从多条轨迹中选择最优轨迹(基于评分机制)
    
    评分标准:
    1. 与参考waypoint的偏离程度 (-min_dist * 10)
    2. 轨迹平滑性 (-dyaw_sum * 5)
    3. 速度合理性 (负速度惩罚)
    4. 与当前速度的匹配度 (-|avg_speed - ego_speed|)
    """
    
    scores = []
    ref_points = waypoints[:12, :2]
    
    for i in range(num_candidates):
        traj = trajectories[i]
        score = 0.0
        
        # 偏离惩罚
        for j in range(min(12, len(traj))):
            dx = traj[j, 0] - ref_points[:, 0]
            dy = traj[j, 1] - ref_points[:, 1]
            dist = np.sqrt(dx**2 + dy**2)
            score -= np.min(dist) * 10.0
        
        # 平滑性惩罚
        for j in range(1, len(traj)):
            dyaw = abs(traj[j, 2] - traj[j-1, 2])
            score -= dyaw * 5.0
        
        # 速度合理性
        for j in range(len(traj)):
            if traj[j, 3] < 0:
                score -= abs(traj[j, 3]) * 10.0
        
        # 速度匹配度
        avg_speed = np.mean(traj[:, 3])
        score -= abs(avg_speed - ego_speed) * 1.0
        
        scores.append(score)
    
    best_idx = np.argmax(scores)
    return best_idx, trajectories[best_idx]

环境配置

python 复制代码
carla_params = {
    'number_of_vehicles': 50,
    'number_of_walkers': 0,
    'dt': 0.1,
    'ego_vehicle_filter': 'vehicle.tesla.model3',
    'surrounding_vehicle_spawned_randomly': True,
    'port': 2000,
    'town': 'Town03',
    'max_time_episode': 3000,
    'max_waypoints': 12,
    'visualize_waypoints': True,  # 显示环境参考轨迹(绿色)
    'desired_speed': 8,
    'max_ego_spawn_times': 200,
    'view_mode': 'top',
    'traffic': 'off',
    'lidar_max_range': 50.0,
    'max_nearby_vehicles': 5,
}

测试流程

复制代码
1. 连接CARLA环境
   └─> gym.make('carla-v0', params=carla_params)

2. 加载PlanningActor模型
   └─> actor.load_state_dict(torch.load('params_dql_planning/actor_200.pth'))

3. 环境重置
   └─> obs = env.reset()

4. 仿真循环 (max_steps=3000)
   │
   ├─> 获取状态向量
   │   └─> state = convert_obs_dict_to_vector(obs) → (307,)
   │
   ├─> 模型推理
   │   └─> action, predicted_trajectory = actor(state)
   │       ├─> action: (3,) [throttle, steer, brake]
   │       └─> predicted_trajectory: (20, 4) [x, y, yaw, speed]
   │
   ├─> 自适应转向限制
   │   ├─> curvature = compute_road_curvature(waypoints)
   │   ├─> steer_limit = get_adaptive_steer_limit(curvature)
   │   └─> action[1] = clip(action[1], -steer_limit, steer_limit)
   │
   ├─> 多轨迹采样
   │   └─> candidate_trajectories = sample_multiple_trajectories(actor, state, n_samples=8)
   │       └─> (8, 20, 4)
   │
   ├─> 最优轨迹选择
   │   └─> best_idx, best_trajectory = select_best_trajectory(candidate_trajectories, waypoints, ego_speed)
   │
   ├─> 执行动作
   │   └─> next_obs, reward, cost, done, info = env.step(action)
   │
   ├─> 可视化
   │   ├─> 候选轨迹(半透明蓝色,8条)
   │   ├─> 最优轨迹(红色,已注释)
   │   └─> 环境参考轨迹(绿色,12点,自动显示)
   │
   └─> if done: break

5. 仿真完成
   └─> env.close()

数据集加载

训练数据集

HDF5数据结构
python 复制代码
data = {
    'observations': (N, 307),          # 观测向量
    'actions': (N, 3),                 # 动作 [throttle, steer, brake]
    'rewards': (N, 1),                 # 奖励
    'next_observations': (N, 307),     # 下一观测
    'dones': (N, 1),                   # 是否结束
    'info/is_collision': (N, 1),       # 是否碰撞
    'info/is_off_road': (N, 1),        # 是否偏离道路
}
观测向量结构 (307维)
复制代码
[0-8]     ego_state (9维)
  - [0]: 位置 x (全局坐标系)
  - [1]: 位置 y (全局坐标系)
  - [2]: 航向角 yaw (弧度)
  - [3]: 速度 (m/s)
  - [4]: 角速度 z (rad/s)
  - [5]: 加速度 x (m/s²)
  - [6]: 加速度 y (m/s²)
  - [7]: 前方距离 (m)
  - [8]: 前车速度差 (m/s)

[9-10]    lane_info (2维)
  - [9]: 车道宽度
  - [10]: 横向偏移

[11-250]  lidar (240维)
  - 激光雷达探测距离

[251-270] nearby_vehicles (20维)
  - 5个周围车辆 × 4维 [dist, speed, dx, dy]

[271-306] waypoints (36维)
  - 12个路径点 × 3维 [local_x, local_y, local_yaw]
轨迹目标提取
python 复制代码
def extract_trajectory_from_obs(obs, trajectory_len=20, point_dim=4):
    """
    从观测中提取轨迹目标
    
    流程:
    1. 提取waypoints (最后36维) → (B, 12, 3)
    2. 前12个点使用真实waypoint坐标和航向
    3. 目标速度使用自车速度 (obs[:, 3])
    4. 后8个点通过外推获得(沿最后有效waypoint方向延伸,间距2米)
    """
    
    trajectory = np.zeros((batch_size, trajectory_len, point_dim))
    
    # 前12个点
    trajectory[:, :12, :3] = waypoints_raw
    trajectory[:, :, 3] = ego_speed  # 所有点速度相同
    
    # 后8个点外推
    for i in range(batch_size):
        last_idx = find_last_valid_waypoint(waypoints_raw[i])
        if last_idx >= 1:
            dir_vec = waypoints_raw[i, last_idx, :2] - waypoints_raw[i, last_idx-1, :2]
            dir_vec = dir_vec / np.linalg.norm(dir_vec)
            
            for j in range(12, trajectory_len):
                offset = (j - last_idx) * 2.0
                trajectory[i, j, :2] = waypoints_raw[i, last_idx, :2] + dir_vec * offset
                trajectory[i, j, 2] = waypoints_raw[i, last_idx, 2]
奖励计算
python 复制代码
def compute_reward_from_obs(obs, is_collision=None, is_off_road=None, desired_speed=8.0):
    """
    从观测计算奖励(模仿carla_env._get_reward)
    
    奖励组成:
    1. 正向驾驶奖励: +1.0 * speed (如果speed <= desired_speed)
                      -1.0 * (speed - desired_speed) (超速惩罚)
    2. 车道偏离惩罚: -1.0 * |lateral_offset|
    3. 横向加速度惩罚: -0.5 * |a_lat|
    4. 静止惩罚: -1.0 (前方无车但速度<0.1)
    5. 碰撞惩罚: -100.0
    6. 偏离道路惩罚: -100.0
    """
数据采样
python 复制代码
class DataSampler:
    def __init__(self, data, device):
        self.data = data
        self.device = device
        self.n_samples = len(data['observations'])
    
    def sample(self, batch_size):
        idx = np.random.randint(0, self.n_samples, batch_size)
        
        state = torch.FloatTensor(self.data['observations'][idx]).to(device)
        action = torch.FloatTensor(self.data['actions'][idx]).to(device)
        next_state = torch.FloatTensor(self.data['next_observations'][idx]).to(device)
        
        # 计算奖励
        reward = compute_reward_from_obs(
            state.cpu().numpy(),
            is_collision=self.data['is_collision'][idx],
            is_off_road=self.data['is_off_road'][idx]
        ).reshape(-1, 1)
        reward = torch.FloatTensor(reward).to(device)
        
        not_done = torch.FloatTensor(1 - self.data['dones'][idx]).reshape(-1, 1).to(device)
        
        # 提取轨迹目标
        trajectory_target = torch.FloatTensor(
            extract_trajectory_from_obs(state.cpu().numpy())
        ).to(device)
        
        return state, action, next_state, reward, not_done, trajectory_target

测试数据集

实时观测获取
python 复制代码
# CARLA环境观测字典
obs = {
    'ego_state': (9,),           # 自车状态
    'lane_info': (2,),           # 车道信息
    'lidar': (240,),             # 激光雷达
    'nearby_vehicles': (20,),    # 周围车辆
    'waypoints': (36,),          # 路径点
}

# 转换为训练格式
state = convert_obs_dict_to_vector(obs)
# state: (307,) 与训练时一致

模型输出

训练阶段

Actor输出
python 复制代码
# 模型输入
state: (B, 307)  # 状态向量

# 模型输出
action: (B, 3)              # [throttle, steer, brake]
trajectory: (B, 20, 4)      # [x, y, yaw, speed]
Critic输出
python 复制代码
# 模型输入
state: (B, 307)  # 状态向量
action: (B, 3)   # 动作向量

# 模型输出
q1: (B, 1)  # Q值估计1
q2: (B, 1)  # Q值估计2
q_min: (B, 1) = min(q1, q2)  # 保守估计
损失计算
python 复制代码
# Critic损失 (TD误差)
critic_loss = F.mse_loss(current_q1, target_q) + F.mse_loss(current_q2, target_q)

# Actor损失 (多目标)
action_loss = F.mse_loss(new_action, action)                    # BC损失
trajectory_loss = F.mse_loss(new_trajectory, trajectory_target) # 轨迹损失
q_loss = -q_new.mean() / q_abs.mean().detach()                  # Q损失(最大化价值)

total_loss = action_loss + eta * q_loss + trajectory_loss_weight * trajectory_loss

推理阶段

Actor推理
python 复制代码
# 输入
state: (307,)  # 单个状态向量

# 输出
action: (3,)              # [throttle, steer, brake]
trajectory: (20, 4)       # [x, y, yaw, speed]
多轨迹采样
python 复制代码
# 输入
state: (307,)
n_samples: 8

# 输出
candidate_trajectories: (8, 20, 4)  # 8条候选轨迹
最优轨迹选择
python 复制代码
# 输入
candidate_trajectories: (8, 20, 4)
waypoints: (12, 3)
ego_speed: float

# 输出
best_idx: int              # 最优轨迹索引
best_trajectory: (20, 4)   # 最优轨迹

控制输出

直接控制模式
python 复制代码
action_dict = {
    'action': [throttle, steer, brake],
}
env.step(action_dict)

代码解读

训练脚本关键代码

1. Actor-Critic架构
python 复制代码
class Diffusion_QL_Planning(object):
    def __init__(self, state_dim, action_dim, max_action, device, ...):
        # 规划型Actor(共享backbone + 双头输出)
        self.actor = PlanningActor(state_dim, action_dim, trajectory_len, point_dim).to(device)
        
        # 双Q网络Critic
        self.critic = Critic(state_dim, action_dim).to(device)
        self.critic_target = nn.ModuleList([copy.deepcopy(self.critic), copy.deepcopy(self.critic)])
2. Critic更新(TD学习)
python 复制代码
# 当前Q值
current_q1, current_q2 = agent.critic(state, action)

# 目标Q值(使用目标策略)
with torch.no_grad():
    next_action, _ = agent.actor(next_state)
    next_action = torch.clamp(next_action, -agent.max_action, agent.max_action)
    target_q1, target_q2 = agent.critic(next_state, next_action)
    target_q = torch.min(target_q1, target_q2)
    target_q = (reward + not_done * agent.discount * target_q).detach()

# TD误差损失
critic_loss = F.mse_loss(current_q1, target_q) + F.mse_loss(current_q2, target_q)
3. Actor更新(多目标优化)
python 复制代码
# 获取新动作和轨迹
new_action, new_trajectory = agent.actor(state)
new_action = torch.clamp(new_action, -agent.max_action, agent.max_action)

# 三种损失
action_loss = F.mse_loss(new_action, action)                    # BC损失
trajectory_loss = F.mse_loss(new_trajectory, trajectory_target) # 轨迹损失

# Q损失(交替使用Q1/Q2,防止过拟合)
q1_new, q2_new = agent.critic(state, new_action)
if np.random.uniform() > 0.5:
    q_loss = -q1_new.mean() / q2_new.abs().mean().detach()
else:
    q_loss = -q2_new.mean() / q1_new.abs().mean().detach()

# 加权求和
total_loss = action_loss + agent.eta * q_loss + agent.trajectory_loss_weight * trajectory_loss
4. 软更新目标网络
python 复制代码
for param, target_param in zip(agent.critic.parameters(), agent.critic_target[0].parameters()):
    target_param.data.copy_(agent.tau * param.data + (1 - agent.tau) * target_param.data)

测试脚本关键代码

1. 自适应转向控制
python 复制代码
# 计算道路曲率
waypoints = obs['waypoints'].reshape(12, 3)
curvature = compute_road_curvature(waypoints)

# 根据曲率调整转向限制
steer_limit = get_adaptive_steer_limit(curvature, base_limit=0.3, max_limit=1.0)

# 限制转向角度
action[1] = np.clip(action[1], -steer_limit, steer_limit)
2. 多轨迹采样策略
python 复制代码
def sample_multiple_trajectories(actor, state, device, n_samples=8):
    """
    通过添加噪声生成多样性轨迹:
    - 输入噪声:增加模型输入的扰动
    - 输出噪声:增加轨迹输出的多样性
    """
    
    trajectories = []
    with torch.no_grad():
        for _ in range(n_samples):
            # 输入噪声(小幅度高斯噪声)
            noisy_state = state_tensor + torch.randn_like(state_tensor) * 0.05
            
            # 模型推理
            _, trajectory = actor(noisy_state)
            traj = trajectory.cpu().numpy().squeeze()
            
            # 输出噪声(位置、航向、速度分别添加不同幅度噪声)
            traj[:, :2] += np.random.randn(20, 2) * 0.1   # 位置噪声较小
            traj[:, 2] += np.random.randn(20) * 0.05      # 航向噪声更小
            traj[:, 3] += np.random.randn(20) * 0.5       # 速度噪声较大
            
            trajectories.append(traj)
    
    return np.array(trajectories)
3. 轨迹评分机制
python 复制代码
def select_best_trajectory(trajectories, waypoints, ego_speed):
    """
    多准则评分:
    1. 路径跟随性:与参考waypoint的最小距离
    2. 轨迹平滑性:相邻点航向角变化
    3. 速度合理性:负速度惩罚
    4. 速度匹配度:与当前速度的差异
    """
    
    scores = []
    ref_points = waypoints[:12, :2]
    
    for traj in trajectories:
        score = 0.0
        
        # 路径跟随性
        for j in range(min(12, len(traj))):
            dx = traj[j, 0] - ref_points[:, 0]
            dy = traj[j, 1] - ref_points[:, 1]
            dist = np.sqrt(dx**2 + dy**2)
            score -= np.min(dist) * 10.0
        
        # 平滑性
        for j in range(1, len(traj)):
            dyaw = abs(traj[j, 2] - traj[j-1, 2])
            score -= dyaw * 5.0
        
        # 速度合理性
        for j in range(len(traj)):
            if traj[j, 3] < 0:
                score -= abs(traj[j, 3]) * 10.0
        
        # 速度匹配度
        avg_speed = np.mean(traj[:, 3])
        score -= abs(avg_speed - ego_speed) * 1.0
        
        scores.append(score)
    
    best_idx = np.argmax(scores)
    return best_idx, trajectories[best_idx]
4. 轨迹可视化
python 复制代码
# 可视化候选轨迹(半透明蓝色)
for i in range(n_trajectory_samples):
    if i == best_idx:
        continue
    
    traj = candidate_trajectories[i]
    for j in range(len(traj)):
        # 局部坐标转全局坐标
        gx = np.cos(ego_yaw_rad) * traj[j, 0] - np.sin(ego_yaw_rad) * traj[j, 1] + ego_x
        gy = np.sin(ego_yaw_rad) * traj[j, 0] + np.cos(ego_yaw_rad) * traj[j, 1] + ego_y
        
        env.world.debug.draw_point(
            carla.Location(x=gx, y=gy, z=ego_transform.location.z + 1.5),
            size=0.08,
            life_time=0.5,
            color=carla.Color(0, 50, 150, a=100)  # 半透明蓝色
        )

架构对比

与 BC Planning 的对比

方面 DQL Planning BC Planning
算法类型 Actor-Critic (RL + BC混合) Diffusion BC (纯模仿学习)
Critic网络 ✅ 双Q网络 ❌ 无
目标网络 ✅ 软更新 ❌ 无
奖励信号 ✅ 显式计算 ❌ 无
Q值优化 ✅ 最大化动作价值 ❌ 无
轨迹采样 ❌ 直接输出 ✅ Diffusion采样
多轨迹选择 ✅ 测试时添加噪声采样 ✅ Diffusion采样天然多模态
训练数据 需要奖励/下一状态 仅需状态/动作
数据效率 较低(需要探索) 较高(纯模仿)
稳定性 较低(RL不稳定) 较高(模仿学习稳定)

数据流对比

DQL Planning 数据流
复制代码
训练阶段:
HDF5文件
    ↓
DataSampler.sample()
    ↓
(state, action, next_state, reward, not_done, trajectory_target)
    ↓
Critic更新:
    critic(state, action) → Q1, Q2
    actor(next_state) → next_action
    target_Q = reward + γ * min(Q_target(next_state, next_action))
    critic_loss = MSE(Q1, target_Q) + MSE(Q2, target_Q)
    ↓
Actor更新:
    actor(state) → new_action, new_trajectory
    action_loss = MSE(new_action, action)
    trajectory_loss = MSE(new_trajectory, trajectory_target)
    q_loss = -Q(new_action).mean() / Q_abs.mean()
    total_loss = action_loss + η*q_loss + λ*trajectory_loss

测试阶段:
CARLA环境 → obs → state
    ↓
actor(state) → action, trajectory
    ↓
自适应转向限制
    ↓
多轨迹采样 → 8条候选轨迹
    ↓
select_best_trajectory() → 最优轨迹
    ↓
env.step(action)
BC Planning 数据流
复制代码
训练阶段:
HDF5文件 → ReplayBuffer → (state, action, trajectory_target)
    ↓
Diffusion_BC_Planning.train():
    q_sample(action, t, noise) → x_noisy
    model(x_noisy, t, state) → pred_action, pred_trajectory
    bc_loss = MSE(pred_action, noise/action)
    trajectory_loss = MSE(pred_trajectory, trajectory_target)
    total_loss = bc_loss + 0.5*trajectory_loss

测试阶段:
CARLA环境 → obs → state
    ↓
agent.select_action(state):
    Diffusion.sample(state) → action
    model(action, t=0, state) → trajectory
    ↓
IDM速度调整
    ↓
env.step({'action': action, 'trajectory': trajectory})

关键差异

方面 DQL Planning BC Planning
优化目标 BC损失 + Q损失 + 轨迹损失 BC损失 + 轨迹损失
价值估计 使用Critic评估动作价值 无价值估计
探索机制 通过Q损失隐式探索 通过Diffusion噪声探索
奖励设计 显式奖励函数 无奖励函数
目标策略 使用Actor预测下一动作 无目标策略
测试控制 直接控制 + 自适应限制 轨迹控制 + IDM
多轨迹 测试时添加噪声生成 Diffusion采样天然多模态
稳定性 需要调参,可能不稳定 相对稳定

总结

训练脚本特点

  1. Actor-Critic架构:结合模仿学习和强化学习

    • Actor:预测动作和轨迹
    • Critic:评估动作价值(双Q网络防止高估)
  2. 多目标损失

    • BC损失:保证策略模仿专家行为
    • Q损失:最大化动作价值(强化学习信号)
    • 轨迹损失:保证轨迹预测准确性
  3. 奖励计算:从观测中计算奖励,无需环境交互

    • 正向驾驶奖励、车道偏离惩罚、碰撞惩罚等
  4. 软更新目标网络:提高训练稳定性

测试脚本特点

  1. 自适应转向限制:根据道路曲率动态调整转向角度

    • 直道:限制转向,保持稳定
    • 弯道:允许更大转向角度
  2. 多轨迹采样:通过添加噪声生成多样性候选轨迹

    • 输入噪声:增加模型输入扰动
    • 输出噪声:增加轨迹多样性
  3. 最优轨迹选择:基于多准则评分选择最佳轨迹

    • 路径跟随性、平滑性、速度合理性、速度匹配度
  4. 轨迹可视化:显示候选轨迹(半透明蓝色)和最优轨迹

架构优势

  1. 混合学习:结合模仿学习的稳定性和强化学习的探索能力
  2. 规划能力:同时预测动作和轨迹,提供未来规划信息
  3. 鲁棒性:多轨迹采样和选择提高决策鲁棒性
  4. 自适应控制:根据道路情况动态调整控制参数

架构挑战

  1. 训练稳定性:RL部分可能导致训练不稳定
  2. 数据效率:需要足够的离线数据训练Critic
  3. 超参数敏感:Q损失权重(eta)和轨迹损失权重需要精细调参
  4. 噪声设计:多轨迹采样的噪声幅度需要合理设计

文件路径

  • 训练脚本: EasyCarla-RL/example/train_offline_dql_planning.py
  • 测试脚本: EasyCarla-RL/example/run_dql_planning_with_trajectory.py
  • 数据集: EasyCarla-RL/example/easycarla_offline_dataset.hdf5
  • 模型保存: EasyCarla-RL/example/params_dql_planning/