EasyCarla-RL DQL Planning 架构设计文档
目录
- 训练脚本架构 (train_offline_dql_planning.py)
- 测试脚本架构 (run_dql_planning_with_trajectory.py)
- 数据集加载
- 模型输出
- 代码解读
- 架构对比
训练脚本架构
整体架构
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采样天然多模态 |
| 稳定性 | 需要调参,可能不稳定 | 相对稳定 |
总结
训练脚本特点
-
Actor-Critic架构:结合模仿学习和强化学习
- Actor:预测动作和轨迹
- Critic:评估动作价值(双Q网络防止高估)
-
多目标损失:
- BC损失:保证策略模仿专家行为
- Q损失:最大化动作价值(强化学习信号)
- 轨迹损失:保证轨迹预测准确性
-
奖励计算:从观测中计算奖励,无需环境交互
- 正向驾驶奖励、车道偏离惩罚、碰撞惩罚等
-
软更新目标网络:提高训练稳定性
测试脚本特点
-
自适应转向限制:根据道路曲率动态调整转向角度
- 直道:限制转向,保持稳定
- 弯道:允许更大转向角度
-
多轨迹采样:通过添加噪声生成多样性候选轨迹
- 输入噪声:增加模型输入扰动
- 输出噪声:增加轨迹多样性
-
最优轨迹选择:基于多准则评分选择最佳轨迹
- 路径跟随性、平滑性、速度合理性、速度匹配度
-
轨迹可视化:显示候选轨迹(半透明蓝色)和最优轨迹
架构优势
- 混合学习:结合模仿学习的稳定性和强化学习的探索能力
- 规划能力:同时预测动作和轨迹,提供未来规划信息
- 鲁棒性:多轨迹采样和选择提高决策鲁棒性
- 自适应控制:根据道路情况动态调整控制参数
架构挑战
- 训练稳定性:RL部分可能导致训练不稳定
- 数据效率:需要足够的离线数据训练Critic
- 超参数敏感:Q损失权重(eta)和轨迹损失权重需要精细调参
- 噪声设计:多轨迹采样的噪声幅度需要合理设计
文件路径
- 训练脚本:
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/