简介面向强化学习研究者和MATLAB/Simulink用户的多智能体路径跟踪控制示例包演示如何通过演员-评论家Actor-Critic与车道保持辅助LKA两类算法训练多个智能体沿预设路径协同运动。压缩包共7个文件以MATLAB脚本.m、实时脚本.mlx、Simulink模型.slx和已训练模型数据.mat为主整体约595KB便于直接加载与二次开发。包内提供智能体创建、参数配置、环境重置与训练入口直接运行实时脚本即可观察完整训练流程也可在Simulink模型中调整奖励函数、探索策略等参数理解多智能体系统建模与仿真关键环节。目前已有1128人学习或下载适合希望快速上手多智能体强化学习路径跟踪、掌握算法选择与系统集成的开发者参考。1. 拆包记TrainMultipleAgentsForPathFollowingControlExample 里到底有什么先说结论这不是一个点开就能跑的玩具 demo而是一套值得反复拆解的强化学习多智能体路径跟踪训练示例包。我拿到 TrainMultipleAgentsForPathFollowingControlExample.zip 后第一反应是先解开目录因为“多智能体”三个字往往意味着代码里不止一个策略网络在并行更新。打开后发现果然如此——一个智能体管方向盘横向控制另一个智能体管油门刹车纵向控制两者共享同一个被控车辆模型在 Simulink 环境里同时训练目标是让车辆沿着一条参考路径稳定行驶。这个包适合三类人刚学完单智能体强化学习、想看看连续动作空间里两个 agent 怎么协作的入门者做车辆横向控制预研、需要快速跑通一个 DDPG/TD3 基线的从业者以及被“路径跟随”这个仿真场景吸引、又不想从零搭环境模型的工程师。它解决的问题很具体在连续动作空间里如何把方向盘控制与速度控制拆给两个独立智能体并且让它们在同一套环境下各自收敛而不是互相干扰。下一章从“为什么要拆成两个”讲起因为这是整个示例包设计逻辑的根。2. 为什么要拆成两个智能体方向盘与油门的强化学习分工2.1 路径跟踪难题横向误差与纵向速度根本是两种物理尺度路径跟随控制表面看是一个“让车沿着线走”的问题但掰开来看横向和纵向控制的物理特性完全不同。横向控制关注的是车距离参考路径的横向偏差、航向角偏差以及前轮转角的变化。它要求系统对位置误差做出快速反应——偏差大时打方向盘拉回来偏差小时微调保持方向。纵向控制关注的是车速和目标速度的偏差、加速度变化需要控制油门与刹车它通常比横向控制慢半拍而且要兼顾舒适性和稳定性。如果把这两个任务塞进同一个策略网络会出现一个常见的麻烦动作输出维度里既有转角又有油门值两者的量纲完全不同。转角 ±0.5 rad 就已经很大了而油门归一化到 0~1 之间。训练时会发现奖励被某个维度主导另一个维度学不到东西。示例包拆成两个智能体本质上是一种动作空间解耦Action Space Decoupling的做法。我在实际跑这个例子时对比过单智能体和双智能体的训练曲线。单个 agent 在前期会把油门的探索噪声误认为是转向信号导致车辆横向摆动幅度偏大拆开之后转向 agent 不再受油门动作维度干扰学习曲线明显更干净。如果你的控制对象也有两个时间尺度差异明显的子任务这个拆法值得直接借鉴。对比维度单智能体动作合并双智能体动作解耦动作维度转角 油门量纲不同每个 agent 输出单一动作奖励来源横向误差 速度误差混合各 agent 独立奖励目标清晰探索效率动作维度高探索慢低维动作空间收敛更快训练稳定性易出现奖励相互抵消奖励互不干扰调试容易2.2 为什么选 DDPG 和 TD3连续控制不是 Q-learning 能直接搞定的这个示例包的默认算法是 DDPGDeep Deterministic Policy Gradient它是深度确定性策略梯度的代表算法专门为连续动作空间设计。你在代码里会看到两个 agent 都用类似的结构但也可以把其中一个替换成 TD3Twin Delayed DDPG。我的经验是TD3 在这个场景下往往比 DDPG 更稳因为 TD3 用两个 Critic 网络取最小值来抑制 Q 值过估计路径跟踪这种要对误差连续加罚的任务很容易让 Q 值虚高TD3 能兜住这一点。如果你拿到包后想换算法核心改动点只有两部分一是把rlDDPGAgent换成rlTD3Agent二是给探索噪声添加TargetUpdateFrequency之类的参数。其他环境、奖励、训练流程完全复用。这也是这套包做得很省事的地方——它把算法层和环境层拆得比较干净。% 以 DDPG 为例创建一个转向控制 agent obsSpec rlNumericSpec([6 1]); % 6 维观测横向误差、航向误差等 actSpec rlNumericSpec([1 1]); % 1 维动作前轮转角 actSpec.LowerLimit -0.5; actSpec.UpperLimit 0.5; % actor 网络观测 - 动作 actorNetwork [ featureInputLayer(6, Normalization, none) fullyConnectedLayer(64) reluLayer fullyConnectedLayer(64) reluLayer fullyConnectedLayer(1, Name, action) tanhLayer scalingLayer(Scale, 0.5) % 把 tanh 输出映射回 [-0.5, 0.5] ]; actor rlContinuousDeterministicActor(actorNetwork, obsSpec, actSpec); agent rlDDPGAgent(actor, [], obsSpec, actSpec);这段代码里scalingLayer(Scale, 0.5)是关键。tanh 层输出的范围是 [-1, 1]所以必须用缩放层把动作范围压到车辆模型能接受的转角范围。我之前手动在动作层后面做过乘法但后续加载网络时容易忘记归一化。直接在 agent 定义里用scalingLayer训练和部署时动作映射是一致的少踩很多坑。2.3 观测共享与独立动作两个智能体如何在一个环境里同时工作拆成两个智能体不代表完全隔离。示例包里两个 agent 面对的观测里有重叠部分比如都接收车辆当前速度、横向偏差、航向偏差但每个 agent 只输出各自维度的动作。Simulink 环境在每一个时间步分别把观测向量发给两个 agent两个 agent 各自计算动作然后共同作用于车辆模型一个控制转角一个控制加速度环境推进到下一步。这个架构看起来绕实际上解决了一个现实问题如果完全隔离转向 agent 看不到车速信息它就无法根据当前车速调整转向幅度——高速时同样的转角偏差会带来更大的横向偏移。示例包让观测部分共享、动作完全解耦等于既保留了协作信息又避免了动作干扰这个设计取舍值得后续自己搭多智能体任务时照搬。我在改自己的场景时也尝试过把两个 agent 的观测完全独立转向 agent 只看横向误差纵向只看速度偏差结果横向控制器在弯道里剧烈摆动因为缺少速度信息导致转向增益不合适。恢复共享观测后问题立刻消失。所以“观测可共享、动作要解耦”算是一条实践经验。3. 环境与奖励函数把路径跟随翻译成强化学习的关键映射3.1 从 Simulink 环境到智能体接口采样周期与控制信号这个示例包跑在 Simulink 环境里车辆动力学采用的是简化自行车模型bicycle model状态包括车辆横纵坐标、航向角、纵向速度等。环境在每个采样周期接收两个动作信号推进一次仿真。采样周期我一般设为 0.05 秒20Hz这个频率对路径跟随足够快同时不会让单回合仿真步数爆炸。如果采样周期太小训练会非常慢如果太大比如 0.2 秒智能体会因为控制间隔过长而难以收敛。接口部分示例包已经帮我们做好了Simulink 模型里暴露两个输入端口转角、加速度通过观测端口输出误差信号。你的核心工作不是搭模型而是确认每一步的观测有没有被正确归一化。% 环境接口定义用 rlSimulinkEnv 连接模型与智能体 mdl multiAgentPathFollowing; obsInfo rlNumericSpec([6 1]); % 6 维观测每个维度含义见注释 actInfo1 rlNumericSpec([1 1]); % 转向 agent 动作 actInfo2 rlNumericSpec([1 1]); % 纵向 agent 动作 env rlSimulinkEnv(mdl, [mdl /RL Agent], obsInfo, [actInfo1 actInfo2]); env.ResetFcn (in) localResetFcn(in); % 每回合重置初始位置/速度localResetFcn负责在每个训练回合开始时随机化初始条件。我一般会把车辆初始横向误差随机化在 ±1.5 米之间初始速度在目标速度的 80%~120% 之间。这样智能体才能在不同的起始状态下学会稳定控制而不是只能处理单一固定起点。3.2 奖励函数设计误差项、平滑项与惩罚项的权重取舍奖励函数是这套示例包里最重要的模组。路径跟踪的奖励设计几乎遵循一个固定框架对需要压小的误差给二次型惩罚对不希望出现的动作幅度给惩罚项两者按权重叠加。转向 agent 的典型奖励形式是% 转向 agent 奖励横向误差 转角变化率惩罚 reward_steer -0.8 * latError^2 ... -0.5 * headingError^2 ... -0.1 * steeringAngle^2 ... -0.05 * (steeringAngle - prevSteeringAngle)^2;四项的含义分别是横向偏差越大惩罚越重航向角偏差越大惩罚越重转角本身过大惩罚转角变化太快惩罚这条是为了抑制来回打方向盘的振荡。纵向 agent 的奖励则是% 纵向 agent 奖励速度偏差 加速度惩罚 reward_speed -0.6 * (velocity - v_ref)^2 ... -0.2 * acceleration^2 ... -0.1 * throttle_command^2;奖励权重的比例关系直接影响训练结果。我的血泪经验是横向误差项的权重一定要比航向误差项大否则智能体会优先把车头对准路径但车身还是歪的——横向误差收敛很慢。示例包默认权重偏保守我自己调试时通常把第一项的系数提高到 1.0 以上收敛速度明显改善。3.3 参考路径与回合终止条件的设置策略示例包里的参考路径一般是一条包含直线段和圆弧段的组合轨迹用于同时考察横向控制器的稳态精度和瞬态响应。路径的信息通过插值方式提供给环境Simulink 环境中计算车辆与路径最近点的横向误差。每回合的终止条件有三个到达路径终点、横向误差超过阈值比如 3 米、回合步数达到上限。终止条件设置有个容易忽略的细节横向误差超限阈值不要设太紧否则训练初期智能体一碰到弯道就终止导致采样经验集中在直线段永远学不会过弯。我把阈值从 2 米放宽到 4 米后前期的有效探索步数多出一截收敛曲线肉眼可见地平滑了。function in localResetFcn(in) % 修改 Simulink 模型中的初始条件 blk [mdl /Vehicle Model]; in setInitialState(in, blk, randInitState()); % 随机化参考路径偏移 refBlk [mdl /Reference Path]; in setParameter(in, refBlk, PathOffset, 2.0 * rand() - 1.0); end重置函数里把初始位置和参考路径偏移量都随机化目的是让智能体在一次训练中覆盖更多场景。4. 训练配置与超参数让示例包从“能跑”到“能收敛”4.1 Actor-Critic 网络结构与初始化拿到示例包第一步我是先打开训练脚本看网络层数因为网络结构直接决定了训练速度和稳定性。示例包里 actor 网络采用两层全连接64 个神经元critic 网络稍微复杂一些先处理观测再拼接动作。如果你在复现时发现训练速度非常慢可以先把 64 改成 32 跑起来看效果如果你发现智能体学不会弯道再考虑增加到 128。% critic 网络输入为观测和动作的拼接向量 criticNetwork [ featureInputLayer(7, Normalization, none) % 6 维观测 1 维动作 fullyConnectedLayer(64) reluLayer fullyConnectedLayer(64) reluLayer fullyConnectedLayer(1, Name, QValue) ]; critic rlQValueFunction(criticNetwork, obsSpec, actSpec);注意 critic 输入维度是观测加动作的维度之和因为这个数字写错了我一开始一直报维度不匹配错误。后来养成了习惯新建网络前先把obsInfo.Dimension和actInfo.Dimension打印出来逐个确认。4.2 训练选项回合数、步数上限、探索噪声与学习率示例包的 train 部分使用的是rlTrainingOptions核心参数包括最大回合数、每回合最大步数、经验池大小、小批量大小、学习率等。我按自己调参的经验整理了一张常用的参考表参数示例包默认我的推荐范围说明MaxEpisodes500800~2000回合数太少弯道场景学不透MaxStepsPerEpisode500400~800路径越长步数需要越大ExperienceBuffer1e51e5~5e5经验池大采样多样性更高MiniBatchSize6464~128小批量过大容易震荡LearnRate (actor)1e-41e-4~3e-4actor 学习率一般比 critic 低LearnRate (critic)1e-31e-3~1e-4critic 学习率过高容易出现 Q 值发散NoiseOptions0.10.1~0.3探索噪声太大初期车辆会乱摆有一个参数很容易被忽略NoiseOptions里的衰减率。示例包默认设置探索噪声在训练后期自动降低这是对的。但我遇到过一个情况——噪声衰减太快智能体在 300 回合之后就完全变成确定性策略此时如果 Q 值还没收敛策略就会固化成某个次优行为后面再怎么训都拉不回来。opt rlTrainingOptions(MaxEpisodes, 1000, ... MaxStepsPerEpisode, 500, ... ExperienceBufferLength, 2e5, ... MiniBatchSize, 64, ... LearnRate, 1e-3, ... StopTrainingCriteria, EpisodeReward, ... StopTrainingValue, -80);停止训练的条件我建议写 EpisodeReward 而非只靠固定回合数。路径跟随任务在权重合理的情况下平均回合奖励能稳定提升到一个接近零的负值区间。设定一个合理的停止值可以省掉大量无效训练时间。5. 常见问题与避坑仿真炸掉、奖励长毛、收敛失败的排障清单5.1 训练期崩溃类NaN 数值发散与仿真卡住现象一训练一开始就出现 NaN 奖励Episode 只跑了 1 步就强制终止。原因最常见的是探索噪声范围太大导致动作值超过车辆模型的物理可接受范围。Simulink 模型一旦接收到超大转角或超大加速度车辆状态发散所有误差信号都变成 NaN。解决先检查动作范围设置。转向 agent 的动作上限先设置为 ±0.3 rad 跑通再逐步放开。同时把探索噪声初始值调低到 0.1 以下。我在调试时还发现一个隐蔽点rlNumericSpec里设置了动作范围但 DDPG agent 的 exploration Noise 如果没有配套 clip噪声样本可能超出动作边界。需要在噪声选项里把下限和上限分别设为动作范围。现象二训练到一半Simulink 仿真卡在某个时间步不动CPU 占用居高不下但 Episode 不前进。原因这个通常是数值突变导致的步长收缩模型在一个很短的仿真步里反复尝试。尤其当你把连续求解器用在带状态跳变的被控对象上时容易出现。解决把求解器切换成固定步长离散求解器ode4步长 0.05 秒。如果还出现卡顿检查模型中是否存在代数环在信号线中间加一个单位延迟模块可以解决。5.2 收敛质量类策略震荡与奖励尺度失衡现象三训练了 600 个回合Episode reward 还在 -200 附近震荡横向误差始终在 1 米以上徘徊。原因奖励函数里横向误差权重太弱智能体发现“摆个烂”和“认真控制”的奖励差别不大。这属于典型的奖励信号淹没在噪声里。解决把latError^2的系数从 0.8 提高到 2.0同时把转角惩罚项降低一半。改完后前 100 个回合的 reward 曲线会出现明显抬头。我通常用这种方式快速判断当前的奖励尺度是否合理。现象四训练后期策略稳定了但车辆在直线段出现小幅度左右摆动且在切换目标路径时反应迟钝。原因策略网络在小误差范围内缺乏区分度——当横向误差只有 0.01 米时网络输出的转角变化不足以推动车辆回到中心线。本质是误差信号没做归一化大误差和小误差挤在同一数量级。解决对所有观测做归一化例如把横向误差除以 5 米、航向误差除以 0.5 rad让输入分布在 [-1, 1] 区间附近。这样做还有一个额外好处actor 网络输出的动作变化会平滑很多。现象五智能体学“聪明”了——先快速提速再猛打方向盘过弯横向误差合格但整车姿态极其狼狈。原因横向 agent 只惩罚误差没有惩罚动作变化率所以它倾向于用大转角快速纠偏。这个问题在最开始可能看不出来等训练接近收敛才暴露。解决在转向 agent 奖励里加入(steeringAngle - prevSteeringAngle)^2这一项并给予一定的权重。加了平滑项后轨迹的 rms 横向误差可能略微上升但控制动作的有效性会明显改善这是必要的取舍。6. 后续玩法验证、导出与奖励重塑的实战技巧训练完成之后我一般不会直接拿平均 reward 判断效果因为平均奖励是累计值无法反映路径跟踪的局部表现。我会写一个评估脚本让训练好的策略在一条全新的路径上跑一遍记录每一步的横向误差、航向误差和速度偏差然后计算三个指标横向误差均方根RMS Lateral Error、最大横向误差、速度保持误差。只有这三个指标都落在合理范围内我才会认为“训练真的成功了”。% 加载训练好的 agent 并部署到仿真环境 agent load(trainedAgent.mat, agent).agent; simOpt rlSimulationOptions(MaxSteps, 600); experience sim(env, agent, simOpt); % 从仿真数据中取横向误差时间序列 latError extractData(experience, LateralError); rmsLatError sqrt(mean(latError.^2)); fprintf(RMS Lateral Error: %.3f m\n, rmsLatError);这条验证流程能帮我快速判断“这个 agent 是不是只会在训练路径上耍小聪明”。如果 RMS 横向误差小于 0.2 米说明控制器基本合格。如果大于 0.5 米我会回过去检查奖励权重而不是盲目加长训练时间。示例包里的 agent 还可以直接导出成独立的 MATLAB 函数在脱离 Simulink 环境的情况下部署。具体做法是把 actor 网络的权重导出来getActor(agent)之后用extractNetwork得到一个dlnetwork对象再调用matlabFunction转成可执行函数。这样你就能把训练好的转向策略嵌入到其它仿真流程里甚至可以和 C 代码生成流程衔接起来。奖励重塑也是后续进阶的重要方向。我的习惯是先用“标准权重”跑通全流程然后针对自己关心的指标做微调查看。比如想让过弯更稳定就把横向误差权重保持在原有水平把转角变化率惩罚项上调 50%想让速度保持更精确就提高速度误差惩罚的相对权重。每改一次权重我会记录好对应的 RMS 指标最终形成一张自己的权重-性能对照表。回到开头说的那个教训设计多智能体强化学习流程时最容易忽视的其实是“环境先能跑通再让策略去学”。从那以后我每次拿到一个新的强化学习示例包都会强制先执行一遍零动作输入的开环仿真确认环境本身稳定、信号数值正常再让智能体进入训练环节。这一步看起来多花十分钟实则是规避掉无数长训练周期之后才发现环境 bug 的唯一后悔药。希望这篇笔记帮你在跑这个示例包时少走几条弯路。本文还有配套的精品资源点击获取