ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

MATLAB自动驾驶最小闭环:单车模型+CNN+强化学习实现

MATLAB自动驾驶最小闭环:单车模型+CNN+强化学习实现 简介面向自动驾驶与数学建模爱好者的MATLAB源码包以精简代码演示了自动驾驶核心模块覆盖环境感知、路径规划、决策制定与车辆控制等环节。包内共6个文件以4个.m脚本为主分别实现强化学习、CNN车辆检测、训练数据生成与结果可视化配套README说明文件帮助快速上手整体仅5KB代码精悍、结构清晰已有738人学习下载。借助这些源码可直观理解Actor-Critic等强化学习算法如何参与路径决策卷积神经网络如何完成车辆目标识别并在Simulink与相关工具箱配合下串联从数据生成到车辆控制的完整流程。检测脚本还可作为视觉识别基础便于向车道线检测、交通标志识别等场景拓展适合希望在MATLAB环境中动手实践自动驾驶算法、夯实数学建模与机器学习基础的研究者与竞赛选手。1. 自动驾驶MATLAB最小闭环先让代码跑起来拿到 self_driving 这套 MATLAB 源码大多数人第一反应是打开 README然后被 actor_critic_rl.m、vehihle_detector_cnn.m 这些文件名劝退。我的建议是先跑 generate_data.m。真正跑过你才会发现自动驾驶在 MATLAB 里不是非要 Simulink 整车模型加持一个最小闭环只需要四件事一个带噪声的车辆运动学模型、一个能识别目标的 CNN、一个负责决策的强化学习控制器、一个把数据画出来的可视化脚本。这套源码适合三类人做数学建模和人工智能大作业的学生缺少 MATLAB 参考实现的无人驾驶方向初学者以及想快速验证控制算法的工程师。它是一个可以继续往里面塞改进点的骨架。2. generate_data.m与车辆运动学建模先让车有“状态”2.1 单车模型是决策级仿真默认选择自动驾驶仿真分很多粒度整车动力学模型、单车运动学模型、质点模型。在决策与路径规划层面整车动力学模型太重它对轮胎力、悬架、路面附着系数的依赖会淹没算法本身的调试重点质点模型又太轻丢掉了转向和航向角这些关键状态。所以这类课程设计和工程验证包几乎默认采用自行车模型也叫单车模型。2.1.1 状态方程与参数表单车模型的思路是把四个车轮等效为前、后两个轮子车辆被抽象成一根轴距为 L 的刚体。状态向量通常取[x, y, theta, v, delta]其中 x、y 是车辆在世界坐标系下的位置theta 是航向角v 是纵向速度delta 是前轮等效转角。其连续时间运动学方程[ \dot{x}v\cos\theta,\quad \dot{y}v\sin\theta,\quad \dot{\theta}\frac{v}{L}\tan\delta ]在 MATLAB 里做离散化时dt一般取 0.050.1 秒。dt 太小会导致仿真步数过大、RL 训练变慢dt 太大则转向响应失真容易在变道时出现剧烈的横向摆动。常用参数范围如下参数符号典型值说明轴距L2.52.9 m小型车轴距决定转弯灵敏度控制步长dt0.050.1 s越小越精确训练代价越大巡航速度v615 m/s园区低速场景建议 8 m/s 以下最大前轮转角delta_max0.30.5 rad对应方向盘较大幅度转向测量噪声sigma0.050.3模拟摄像头/雷达观测不确定性这个模型不描述侧滑和轮胎附着但它把“转向输入如何转成轨迹曲率”这个关系表达清楚了足够用来训练决策算法。2.2 generate_data.m如何造出可训练的数据源码包里的 generate_data.m 负责生成车辆轨迹与传感器观测。在真实工程里这一步叫数据闭环的第一步——如果没有一个确定性的环境后面 CNN 和 Actor-Critic 都只能用随机参数瞎试。常见做法是生成一段包含直行、换道、跟车减速的基准轨迹再往观测值里注入高斯噪声模拟摄像头和雷达不完全置信的测量结果。% generate_data.m 核心生成逻辑简化示意 dt 0.1; % 控制步长单位 s v 8.0; % 巡航速度单位 m/s L 2.7; % 轴距单位 m T 40; % 总仿真时长单位 s N T / dt; % 总步数 x(1) 0; y(1) 0; theta(1) 0; % 起点状态 for k 1:N-1 % 前 2 s 直行之后执行一个换道动作 if k * dt 2 delta(k) 0; else delta(k) 0.15; % 前轮转角单位 rad end % 离散化运动学递推 theta(k1) theta(k) v / L * delta(k) * dt; x(k1) x(k) v * cos(theta(k)) * dt; y(k1) y(k) v * sin(theta(k)) * dt; end % 给横向观测叠加高斯噪声模拟传感器不确定性 y_obs y 0.15 * randn(size(y)); % 保存为后续 CNN / RL 共用的标准输入 save(driving_data.mat, x, y, y_obs, theta, dt, v);这段代码的关键有三处delta(k)是唯一可控输入改变它的时间序列就能生成直行、换道、绕障等不同场景v/L*delta(k)*dt这一步把转向量转成航向角增量是单车模型的核心递推y_obs故意叠加噪声是为了让后续 CNN 学到的是“带噪声的观测”而不是在干净数据上过拟合。如果省掉噪声训练出来的检测器和控制器一到真实仿真环境就失效。2.2.1 数据格式与后续接口save出来的 driving_data.mat 是整个项目的公共接口。CNN 训练时把 x、y 轨迹投影到图像坐标再渲染成目标框RL 训练时把 x、y、theta 的差分作为状态特征。这里建议统一采用“世界坐标系存轨迹 图像坐标系存检测标签”的分工世界系负责控制一致性图像系负责视觉验证两者通过外参矩阵转换。2.3 数据环节最容易踩的坑第一个坑是坐标系统一。generate_data.m 产生的是米制世界坐标而 vehihle_detector_cnn.m 的输入是像素图。如果不做[u,v]到[x,y]的换算控制模块拿到的横向偏差会差出几十倍。图像坐标系到世界坐标系的投影转换MATLAB 图像处理工具箱里有现成函数可以直接调用。第二个坑是噪声强度不能一刀切。车速低时 0.3 m 的噪声还能接受车速 15 m/s 时同样噪声会让换道控制直接震荡需要按速度调整sigma。第三个坑是数据集里场景比例失衡如果 80% 都是直行片段CNN 会学成一个“只会输出直行”的分类器训练时准确率虚高放到变道场景完全不可用。提示生成数据时要在脚本末尾固定随机种子例如rng(42)。否则每次跑 generate_data.m 轨迹不同RL 训练结果无法横向对比。3. vehihle_detector_cnn.m与CNN感知把“看见”变成数据3.1 为什么感知层用CNN替代传统检测在MATLAB里做车辆检测老办法是图像差分、边缘提取加形态学闭运算。它们对固定背景有效但光照一变、前车颜色接近路面阈值就失灵。CNN 的思路是让网络自己学特征车灯、车窗、阴影、保险杠轮廓这些高层语义特征不需要人工指定。代价是需要带标签数据和一块能跑深度学习的显卡。对这套源码来说训练数据量不大一张中等性能 GPU 或 MATLAB 支持 CUDA 的环境即可纯 CPU 也能跑小尺寸图只是训练时间会拉长。自动驾驶数据集里“无车”帧通常远多于“有车”帧后面训练时要专门处理类别不平衡。3.2 vehihle_detector_cnn.m的网络结构与训练指令3.2.1 网络拓扑设计vehihle_detector_cnn.m 里搭的是一个轻量 CNN输入为 64×64 的 RGB 图像中间经过两组卷积池化最后接全连接和 softmax 分类层。压缩到 64×64 不是为了检测精度而是为了让训练时间可控跑通闭环后再换更深网络。% 定义轻量 CNN用于前车/背景二分类 layers [ imageInputLayer([64 64 3], Normalization, none, Name, input) convolution2dLayer(3, 16, Padding, same, Name, conv1) reluLayer(Name, relu1) maxPooling2dLayer(2, Stride, 2, Name, pool1) convolution2dLayer(3, 32, Padding, same, Name, conv2) reluLayer(Name, relu2) maxPooling2dLayer(2, Stride, 2, Name, pool2) fullyConnectedLayer(64, Name, fc) reluLayer(Name, relu3) fullyConnectedLayer(2, Name, out) softmaxLayer(Name, prob) classificationLayer(Name, classout) ];这里每组conv relu pool都在做特征压缩conv1 用 16 个 3×3 卷积核提取边缘和纹理pool 把空间尺寸减半fc 层把高层特征映射到 2 个类别。最后一层fullyConnectedLayer(2)对应“前方有车 / 无车”如果检测类别增加到行人、自行车这一维要改成对应类别数。Normalization设为 none是因为图像已经统一缩放到 [-1,1] 或 [0,1]归一化在数据增强阶段做更灵活。3.2.2 训练与模型保存MATLAB 里训练 CNN 不需要手动写反向传播拆开 imageDatastore、定义 trainingOptions、调用 trainNetwork 三步即可。下面代码展示了如何从文件夹数据集建立训练集与验证集% 目录结构建议data/train/vehicle、data/train/background imds imageDatastore(./data/train, IncludeSubfolders, true, ... LabelSource, foldernames); [imdsTrain, imdsVal] splitEachLabel(imds, 0.8, 0.2, randomized); % 增强随机平移和翻转解决样本量不足的问题 aug imageDataAugmenter(RandXTranslation, [-5 5], ... RandYTranslation, [-5 5], RandXReflection, true); augimdsTrain augmentedImageDatastore([64 64 3], imdsTrain, ... DataAugmentation, aug); augimdsVal augmentedImageDatastore([64 64 3], imdsVal); options trainingOptions(sgdm, ... InitialLearnRate, 1e-2, ... MaxEpochs, 30, ... MiniBatchSize, 32, ... Shuffle, every-epoch, ... ValidationData, augimdsVal, ... Plots, training-progress, ... Verbose, false); net trainNetwork(augimdsTrain, layers, options); save(vehicle_cnn.mat, net);训练选项里InitialLearnRate1e-2配合 sgdm 是这类小数据集的稳妥起手。验证集比例 20% 用于观察过拟合训练准确率持续上升而验证准确率停滞说明模型开始背训练集此时应加大增强强度或调小网络。save(vehicle_cnn.mat,net)把网络存成 mat 文件后续 RL 环境加载它的检测结果不需要重新训练。训练选项建议值作用solversgdm小数据集收敛稳定InitialLearnRate1e-2 或 1e-3过高发散过低收敛慢MiniBatchSize1664显存不足时调小MaxEpochs2050看验证曲线决定ValidationFrequency10太大会错过过拟合拐点3.2.3 标签不均衡时怎么处理如果直接按原始比例训练网络会倾向于输出背景类。常见做法是在分类层里设置ClassWeights或者对“有车”类做过采样复制。数据增强里的RandXReflection只对横向对称物体有效对“停车标志”这种不对称目标不要开翻转。训练完成后用classify或predict对单帧图像做推理输出置信度向量。3.3 检测结果如何喂给决策模块检测输出不只是标签还要给出位置信息。vehihle_detector_cnn.m 里对图像做滑窗或候选区域提取得到目标框网络对每个候选框输出置信度再用非极大值抑制 NMS 保留最可能的框。最终传给决策模块的字段是[u, v, w, h, score]u、v 是目标框中心像素坐标score 是置信度。控制模块只关心两件事横向偏差目标中心与车道中心像素差和相对距离框高度反映目标远近。所以感知到决策的接口要约定成固定结构体不能在 RL 训练里不断改字段名。4. actor_critic_rl.m与强化学习控制让车学会决策4.1 Actor-Critic为什么适合无人驾驶控制决策层如果只用 Q-learning动作空间必须离散化比如把转向角离散成左、中、右三档。这在真实车辆控制里太过粗糙换道时容易来回切换乘员感受很差。纯策略梯度方法如 REINFORCE 可以输出连续动作但每一步要跑完整幕才能更新方差很大。Actor-Critic 把两者合并Actor 负责输出动作分布Critic 负责估计状态价值用 TD 误差实时更新方差比 REINFORCE 小又支持连续控制所以这类自动驾驶 MATLAB 源码包选用它是合理的。4.2 源码包的AC网络定义4.2.1 actor网络用于输出动作actor 网络输入状态特征输出一个高斯动作分布的均值。源码包里 actor_critic_rl.m 的手写实现大致按下述结构组织% actor 网络状态 - 动作均值 stateDim 4; % [相对距离误差, 相对速度, 横向偏差, 曲率] actionDim 1; % 期望加速度 w1 0.1 * randn(stateDim, 64); b1 zeros(1, 64); w2 0.1 * randn(64, 32); b2 zeros(1, 32); w3 0.1 * randn(32, actionDim); b3 zeros(1, actionDim); function mu actorForward(x, w1, b1, w2, b2, w3, b3) h1 tanh(x * w1 b1); h2 tanh(h1 * w2 b2); mu h2 * w3 b3; % 连续动作均值 end用 tanh 而不是 relu 作为隐藏层激活是因为动作输出需要被限制在合理区间网络内部保持上下对称的激活更容易让输出稳定。手写权重初始化用 0.1 倍随机防止深层激活值过大。4.2.2 critic网络用于评估状态价值critic 网络结构类似但输出只有一个标量表示当前状态的期望折扣回报。它的输入可以只接状态也可以接“状态动作”再输出 Q 值。源码包里通常采用前者TD 误差的计算更直接% critic 网络状态 - 状态价值 V(s) cw1 0.1 * randn(stateDim, 64); cb1 zeros(1, 64); cw2 0.1 * randn(64, 64); cb2 zeros(1, 64); cw3 0.1 * randn(64, 1); cb3 zeros(1, 1); function v criticForward(s, cw1, cb1, cw2, cb2, cw3, cb3) h1 tanh(s * cw1 cb1); h2 tanh(h1 * cw2 cb2); v h2 * cw3 cb3; endcritic 的输出用来计算 TD 误差td reward gamma * v_next - v。td 为正说明当前动作比预期好Actor 应增大该动作概率td 为负则相反。这就是 Actor-Critic 中两个网络协作的核心机制。4.3 训练主循环与梯度更新规则actor_critic_rl.m 的训练流程是标准的 episode 循环重置环境、采样动作、执行动作、计算奖励、更新两个网络、记录折扣回报。顺序是先采样再更新每一步都算 TD 误差不需要等完整幕结束。gamma 0.95; % 折扣因子 alphaA 1e-3; alphaC 1e-2; % actor/critic 学习率 sigma2 0.1^2; % 探索噪声方差 for ep 1:200 state resetEnv(); % 回到场景起点 done false; epReward 0; while ~done % 1) 从 actor 输出的均值附近采样动作 mu actorForward(state, w1,b1,w2,b2,w3,b3); action mu 0.1 * randn; % 高斯探索 % 2) 在环境中执行一步 [nextState, reward, done] stepEnv(state, action); % 3) 计算 TD 误差 v criticForward(state, cw1,cb1,cw2,cb2,cw3,cb3); vn criticForward(nextState, cw1,cb1,cw2,cb2,cw3,cb3); td reward gamma * vn * (1 - done) - v; % 4) critic 更新TD 误差平方损失负梯度方向 h2 tanh(tanh(state * cw1 cb1) * cw2 cb2); cw3 cw3 - alphaC * td * h2; % 5) actor 更新策略梯度方向 grad_logp (action - mu) / sigma2; w3 w3 alphaA * td * grad_logp * h2; state nextState; epReward epReward reward; end if mod(ep, 10) 0 fprintf(episode %3d | reward %6.2f\n, ep, epReward); end end这里的关键是td被 actor 和 critic 共用critic 把 td 当作回归目标actor 把 td 当作动作优劣信号。探索噪声 0.1 控制“动作不完全是网络输出的均值”训练后期可以逐步衰减。gamma0.95意味着模型只关心未来约十几步内的收益决策更短视、更安全适合跟车和车道保持如果做长距离路径规划再提高 gamma。若不想手推梯度MATLAB 里可直接把权重定义成dlarray用dlfeval自动微分替换第 4、5 步。4.4 奖励函数设计与收敛性检查4.4.1 奖励函数构成示例自动驾驶的奖励函数要同时惩罚“偏离车道”和“跟车过近”多目标奖励需要按数量级拉开权重奖励项表达式权重设计意图车道保持-横向偏差^21.0偏差越大惩罚越重跟车距离-(gap - gap_ref)^20.8保持安全车距平滑性-(加速度差)^20.2防止急加速急减速到达奖励10 到终点10稀疏正奖励引导完成场景把所有惩罚项平方比用绝对值更好因为平方项对小偏差容忍、对大偏差剧烈惩罚能避免车辆在车道边界来回摩擦。权重分配上车道保持和跟车距离优先平滑项只做辅助。提示如果训练时 reward 一直不涨先把所有惩罚项 scale 到 01 之间再调学习率。奖励数值过大TD 误差就大参数更新会震荡奖励数值过小信号被噪声淹没网络学不到东西。4.4.2 训练发散时先查什么第一看探索噪声噪声太大动作一直随机critic 学不到稳定价值。第二看学习率比值actor 学习率一般要比 critic 小一个数量级因为 actor 的梯度信号来自 critic本身更不稳。第三看是否奖励一直为零如果目标点设计得过远episode 内永远到不了稀疏奖励完全缺失需要加 shaping 项。遇到连续发散不要先改网络层数先固定随机种子并减小学习率绝大多数情况可以稳定下来。5. plot_test.m与回放验证确认自动驾驶逻辑真的收敛5.1 验证维度与绘制策略训练完成后光看 episode reward 曲线还不够必须回到几何空间验证。plot_test.m 的作用是把仿真过程逐帧画出来在同一个 figure 里同时展示感知结果和控制轨迹肉眼确认车辆是否压线、检测框是否稳定。工程上至少看三个指标横向偏差的均方根值、累计压线次数、跟车距离误差均值。这三个指标比 reward 数值更接近真实驾驶质量。5.2 把仿真过程录成视频% plot_test.m 核心回放逻辑 for k 1:length(stateSeq) % 左图原始画面叠加检测框 subplot(1,2,1); imshow(frame{k}); hold on; rectangle(Position, bboxSeq{k}, EdgeColor, r, LineWidth, 2); text(bboxSeq{k}(1), bboxSeq{k}(2)-8, ... sprintf(score %.2f, scoreSeq{k}), Color, r); % 右图车辆实际轨迹与参考线 subplot(1,2,2); plot(refX, refY, k--, LineWidth, 1.2); hold on; plot(xHist(1:k), yHist(1:k), b-, LineWidth, 1.6); plot(xHist(k), yHist(k), ro, MarkerSize, 6, MarkerFaceColor, r); axis equal; grid on; drawnow; F(k) getframe(gcf); end % 保存为可直接播放的 avi 文件 writerObj VideoWriter(self_driving_result.avi); open(writerObj); writeVideo(writerObj, F); close(writerObj);rectangle的 Position 来自 CNN 输出的 bboxscore低于 0.5 的目标框应该被过滤掉否则画面上全是噪声框。右侧的refX/refY来自 generate_data.m 保存的参考线。保存视频时用exportgraphics也可以但 getframe 对动态图更直观。5.3 从demo走向可评估场景的边界如果要把这套 MATLAB demo 往工程级场景评价靠拢可以参考 ISO 34505 的思路把测试场景按可重复性、覆盖率和危险度分级组织。具体在本项目里能做三件事固定rng种子保证同一场景可复现把直行、换道、前车切入三类场景分别存成独立 mat 文件逐个跑测试每个场景跑 10 个 episode 取中位数而不是拿单次最好成绩。这样得到的横向偏差和压线次数才具备和后续改进版本对比的统计意义。给你一个排查性能瓶颈的顺序actor_critic_rl.m 是决策核心vehihle_detector_cnn.m 是感知核心generate_data.m 是数据源头。改进时按数据→感知→决策的顺序逐个替换每次只动一个模块重新回放才能定位到底在哪一环掉链子。本文还有配套的精品资源点击获取
返回列表