共计 2982 个字符,预计需要花费 8 分钟才能阅读完成。
背景痛点
传统 CarSim 控制方法(如 PID 控制)需要手动调参,难以应对复杂驾驶场景。而强化学习(Reinforcement Learning)与 CarSim 结合时,新手常遇到三大难题:
- 物理引擎接口复杂:CarSim 的动力学模型包含数百个参数,直接调用底层 API 需要掌握 C ++ 和 Simulink 知识
- 训练效率低下:连续控制任务中,智能体(Agent)探索效率低导致收敛缓慢
- 仿真 - 现实鸿沟:在仿真中训练的策略(Policy)往往难以直接迁移到真实车辆
技术选型
连续控制任务中,主流算法对比:
- DQN:适用于离散动作空间(如游戏按键),无法直接处理油门 / 转向的连续值
- PPO:策略梯度方法,对超参数敏感,训练波动较大
- DDPG:Actor-Critic 架构,适合连续控制,本文选择它的原因:
- 直接输出方向盘转角 / 油门等连续动作
- 通过经验回放池(Replay Buffer)提升样本利用率
- 双网络结构(Actor+Critic)稳定训练过程
实现细节
1. CarSim-Python 接口搭建
推荐通过 Simulink 生成 DLL 动态链接库:
% Simulink 模型配置步骤
1. 在 CarSim 界面导出 VS Vehicle Model
2. 添加 S -Function 模块连接控制输入 / 输出
3. 使用 Simulink Coder 生成 car_sim_interface.dll
Python 端调用示例:
import ctypes
car_sim = ctypes.CDLL('./car_sim_interface.dll')
# 关键参数说明
sim_step = 0.01 # 仿真步长 10ms
def run_step(action):
steer, accel = action # 输入转向角 [-1,1] 和加速度[0,1]
car_sim.set_input(ctypes.c_float(steer), ctypes.c_float(accel))
car_sim.step()
state = get_state() # 获取下一状态
return state
2. 状态空间设计
选取 12 维特征向量:
- 车辆状态:车速(m/s)、横向偏差(m)、航向角误差(rad)
- 道路信息:曲率(1/m)、前方 10m/20m/30m 处的预瞄点偏差
- 历史动作:上一时刻的转向角、加速度
# 状态归一化处理示例
def normalize_state(raw_state):
# raw_state: [vx, vy, yaw_rate, e1, e2,...]
return np.array([raw_state[0]/30.0, # 车速归一化到 0~1
raw_state[3]/3.0, # 横向偏差限制在±3m
raw_state[4]/np.pi # 角度归一化到[-1,1]
# ... 其他特征
])
3. 奖励函数设计
多目标加权组合:
R = w_1(1-|e|) + w_2\cos(Δψ) + w_3a_{smooth} - w_4|δ|
- 轨迹跟踪:横向偏差 e 越小越好
- 行驶舒适性:转向角变化率 a_smooth=‖δ_t – δ_{t-1}‖
- 控制效率:避免过大转向 δ
实际调参建议:
# 奖励计算示例
def calc_reward(state, action, last_action):
e = abs(state[3]) # 横向偏差
delta_steer = abs(action[0] - last_action[0])
reward = 2.0*(1 - e/3.0) - 0.1*delta_steer - 0.05*abs(action[0])
return np.clip(reward, -1, 1) # 限制奖励范围
核心代码实现
DDPG 网络结构
import torch
import torch.nn as nn
class Actor(nn.Module):
def __init__(self, state_dim, action_dim):
super().__init__()
self.net = nn.Sequential(nn.Linear(state_dim, 256),
nn.LayerNorm(256), # 稳定训练
nn.ReLU(),
nn.Linear(256, action_dim),
nn.Tanh() # 输出[-1,1]
)
def forward(self, state):
return self.net(state)
class Critic(nn.Module):
def __init__(self, state_dim, action_dim):
super().__init__()
self.state_net = nn.Sequential(nn.Linear(state_dim, 128),
nn.LayerNorm(128),
nn.ReLU())
self.q_net = nn.Sequential(nn.Linear(128 + action_dim, 128),
nn.ReLU(),
nn.Linear(128, 1)
)
def forward(self, state, action):
s_feat = self.state_net(state)
return self.q_net(torch.cat([s_feat, action], dim=1))
经验回放池
from collections import deque
import random
class ReplayBuffer:
def __init__(self, capacity):
self.buffer = deque(maxlen=capacity) # 循环队列
def add(self, state, action, reward, next_state, done):
self.buffer.append((state, action, reward, next_state, done))
def sample(self, batch_size):
batch = random.sample(self.buffer, batch_size)
states, actions, rewards, next_states, dones = zip(*batch)
return np.array(states), np.array(actions), \
np.array(rewards), np.array(next_states), np.array(dones)
避坑指南
1. CarSim 实时同步
- 设置仿真步长 = 训练步长(建议 10ms)
- 在 Python 端添加循环等待,直到收到 CarSim 的完成信号
while not car_sim.is_step_done():
time.sleep(0.001) # 避免 CPU 跑满
2. 奖励稀疏问题
- 增量奖励:给每个步骤的小改进分配微小奖励
- 课程学习:先训练简单直线场景,再逐步增加弯道复杂度
3. 训练不稳定
- 优先检查梯度爆炸:添加梯度裁剪(
torch.nn.utils.clip_grad_norm_) - 降低学习率(Actor 建议 3e-4,Critic 建议 1e-3)
- 增加 Batch Size(至少 128)
效果验证

– 蓝色轨迹:PID 控制,在急弯处偏离车道
– 红色轨迹:DDPG 策略,始终保持居中行驶
延伸思考
如何将模型部署到真实车辆 ECU?
- 量化压缩:将 PyTorch 模型转换为 TensorRT 引擎
- 硬件适配:使用 NVIDIA Drive 系列芯片满足实时性要求
- 安全冗余:添加看门狗机制(Watchdog)监控决策异常
完整代码已开源:GitHub 链接
正文完
