尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

强化学习实战:基于Unity ML-Agents的自行车多智能体仿真训练

强化学习实战:基于Unity ML-Agents的自行车多智能体仿真训练 简介本资源是一个面向智能交通系统研究者与强化学习实践者的Unity仿真项目聚焦于自行车智能体在复杂城市环境中的自主导航、动态避障与群体协同行为建模。项目基于Unity ML-Agents release 15框架构建完整集成物理仿真、交通规则建模与多智能体训练流程适用于高校科研、自动驾驶子领域实验及RL算法工程化验证等场景。压缩包共566个文件含244个meta配置文件支撑Unity场景与组件元数据、71张jpg纹理与界面图、30个onnx模型导出的训练策略、26个pt权重文件PyTorch训练中间态、14个prefab预制体含Roundabout、X/T型路口等典型交通场景及21个C#脚本控制逻辑与奖励函数实现整体大小为34.12MB。已有71人学习下载资源附带完整说明文档、多组预设训练场景如动态交叉口、环岛、密集车流路段及可直接加载运行的Terrain与AI Bike资产支持开箱即训、策略可视化与群体行为分析是开展交通智能体研究的高复用性实验基座。 把自行车骑进仿真世界这件事听起来像是个游戏Demo但它背后其实是一整条强化学习工程链路。这个项目干的事就是用 Unity 的 ML-Agents 框架release 15 版本训练一批自行车智能体在模拟交通场景里自主导航、躲障碍还要让多辆自行车组成的群体协同跑起来。选这个方向是因为真实道路测试成本高、场景没法重复仿真环境里可以快速迭代策略把危险路况变成可以反复回放的训练样本。这个系统面向三类人很有价值一是做强化学习算法落地的工程师想看看除了经典CartPole之外还能怎么玩二是做机器人导航的朋友多智能体避障和群体协同是躲不开的问题三是做游戏AI的开发者NPC或者载具单位想要更自然的驾驶行为这套方案可以直接迁移。你不需要从零造轮子ML-Agents已经把训练框架、通信协议、算法实现都封装好了你要做的核心工作就是搭环境、定义状态动作和奖励、调参。下面我会按实际开发顺序把整个系统的设计思路、环境搭建、算法配置、训练调试和验证评估一条线讲清楚并把过程中踩过的坑和排查经验一并列出来。1. 项目背景与系统整体设计思路1.1 为什么选 Unity ML-Agents 做仿真很多做强化学习的人首选 gym 或者 mujoco 这类轻量环境但那类环境有个问题场景太抽象视觉上就是几个几何体做机器人导航这类带空间感知的任务效果不够直观。如果只是单智能体在空旷区域走迷宫gym 完全够用。但项目要模拟的是真实交通场景包含道路、信号灯、行人、车辆、多辆自行车之间的交互这需要一套能做精细刚体物理、场景渲染和碰撞检测的引擎Unity 在这方面是现成的。ML-Agents 是 Unity 官方维护的强化学习插件release 15 这个版本已经相当成熟。它最大的优势在于Agent 的场景配置全部在 Unity 编辑器里完成训练则通过 Python 端的 mlagents-learn 命令拉起两者之间靠通信协议交互。这意味着你不用自己写环境交互接口也不用自己实现 PPO 算法框架里都带了。而且它支持多智能体并行训练可以多个 Agent 同时探索环境训练效率比单环境高不少。选它而不是自研仿真器的核心原因是开发成本完全不在一个量级。自研需要处理刚体动力学、碰撞检测、场景管理、数据采集、训练通信每一步都是大工程。Unity 把这些都做好了你只需要关注业务层也就是自行车怎么动、目标怎么设、奖励怎么给。这也正是这个项目能在一个合理周期内跑通的关键。1.2 系统架构与核心模块划分整个系统从逻辑上可以拆成四个大模块仿真场景层包含道路、绿化带、信号灯、障碍物、目标点等静态元素以及行人、机动车、其他自行车这类动态元素。自行车智能体层每辆自行车是一个 Agent包含刚体组件、碰撞体、感知传感器、决策控制脚本。这里要区分感应和控制两部分前者负责收集状态后者负责输出动作。训练与通信层ML-Agents 的 Python 训练器负责采样、计算梯度、更新策略通过 Socket 或通信协议与 Unity 编辑器或打包后的可执行文件双向传输数据。评估与可视化层训练过程中用 TensorBoard 看奖励曲线训练完成后把模型加载回 Unity在真实场景里回放验证。这四个模块里最容易低估工作量的是场景层。很多人以为强化学习的关键在算法和奖励设计但实际跑起来你会发现场景里一个碰撞体没配置好车辆穿模了或者信号灯的逻辑跟真实场景差别太大都会导致训练出来的策略在迁移时完全不适用。所以场景层的精细化程度直接决定了最终策略的上限。1.3 版本选型与工程结构布局ML-Agents release 15 对应的是 Unity 2021 LTS 及以上版本这里要注意版本兼容性问题。插件包导入时如果 Unity 版本过低部分 API 会报找不到命名空间尤其像 UnityEngine.AI 这类模块和 ML-Agents 的交互报错信息不一定直观。工程结构上推荐这样组织Assets/ Scenes/ // 训练场景、评估场景 Scripts/ // Agent控制、场景管理器、信号灯控制器 Config/ // 训练超参数配置 YAML 文件 Models/ // 训练产出的 .onnx 模型文件 Prefabs/ // 自行车预制体、障碍物预制体这里有个习惯要养好场景里所有 Agent 和交互物体都要做成 Prefab不要直接在场景里编辑一堆裸物体。因为训练过程中往往要调参重来如果是裸物体改一处就得整个场景重新拖拽一遍用 Prefab 就只需改一处所有实例同步更新。我最初就是没做 Prefab调了三次碰撞体参数每次都要逐辆自行车改非常浪费时间。2. 仿真场景搭建与自行车运动学建模2.1 自行车智能体的物理模型搭建自行车的物理模型是整个系统的地基。如果模型搭得太粗糙比如只用一个小方块当自行车那学出来的策略根本没有迁移意义如果搭得太精细比如完整模拟链条、踏板、车身倾斜角度训练复杂度会急剧上升前期探索阶段大概率一直撞墙。我采用的方式是折中方案用胶囊体模拟车身两个球体作为前后轮所有部件挂在同一个空物体下由刚体统一控制。刚体质量设为 80kg模拟骑行者加车身总重Drag 设为 0.5Angular Drag 设为 1.5这样在低速场景下不会显得太飘。运动控制上自行车本质是一个非完整约束系统转弯半径与转向角和速度直接相关。在 Unity 里实现时我不直接修改位置而是给刚体施加控制力通过转向角控制绕 Y 轴的扭矩通过油门控制前进方向上的驱动力。具体做法是每一帧读取当前的 forward 方向把油门输入映射为沿该方向的作用力把转向输入映射为施加在刚体上的绕 Y 轴角速度。有个关键参数需要反复试验最大转向角。真实自行车前轮最大转角大概在 40 度左右但在仿真里如果允许这么大转角一方面训练初期智能体容易原地绕圈导致探索效率极低另一方面物理引擎在高速大转角下会产生严重的侧向滑动。我的处理是把转向角限制在 ±25 度后期如果发现策略偏向于低速转弯再逐步放开限制。这个数值范围的设定实际上是给训练过程设了一个合理的探索边界。2.2 复杂道路与交通场景的构建方式场景布局直接决定任务难度。一开始我按真实城市标线把道路做得分毫不差双向四车道加上非机动车道还有两个十字路口。结果训练进展非常缓慢智能体在前 20 万步基本都在原地打转根本没有有效探索。后来我调整了思路先建一个简化的闭合赛道只有直道和两个 U 型弯没有信号灯只要求自行车在赛道内从起点巡航到终点先把这一步学会再去叠加十字路口。这种课程学习的思路是这类项目里非常实用的策略。强化学习训练初期奖励信号如果太稀疏或者场景复杂度太高智能体几乎无法从随机探索中获取有效反馈。先把简单场景学稳再逐级增加复杂度每一步都基于上一步的模型继续训练收敛速度和最终效果都会好很多。交通场景里最值得做的是信号灯系统。我实现的方式是在路口每个方向放置一个 Trigger Zone由信号灯控制器统一管理信号灯的状态机红灯、绿灯、黄灯按时序切换。当自行车进入 Trigger Zone 时读取当前信号灯状态如果红灯且仍在路口区域内就会触发闯红灯惩罚。这里有个小坑Trigger Zone 如果太大自行车在路口外围就触发限制动作空间被过早约束影响正常行驶如果太小高速冲过来的自行车会直接穿过检测区域没被拦住。我把每个方向的检测区域设为 6 米长、覆盖整个车道宽度在高速度下也能可靠触发。2.3 多智能体群体环境的部署要点群体场景和单智能体场景最大的区别在于每个智能体既是学习者又是其他智能体的动态障碍物。在真实交通里自行车群体会互相让行、错位超车这种协调行为在仿真里需要通过大量碰撞惩罚和经验积累涌现出来而不是靠硬编码规则。ML-Agents 支持在一个场景里放置多个 Agent 对象它们共享同一个 Behavior Name就会用同一条策略去控制。这意味着所有自行车用的是同一个神经网络参数学习到的行为是群体共享的。这种设计的好处是训练效率高所有智能体产出的样本都汇入同一个经验池代价是无法出现个体差异化策略。对自行车群体仿真来说共享策略是合理假设因为真实骑手的驾驶风格虽然有差异但基础规则是相近的。部署时要注意一个性能问题每个智能体身上的 RayPerceptionSensorComponent3D 会发射多条射线每辆自行车假设发 12 条射线10 辆自行车就是 120 次射线检测每物理帧。场景里的碰撞体越多射线检测的物理开销越大训练速度会显著下降。我的优化方式是用 Layer 区分碰撞组射线只检测指定 Layer 的物体比如障碍物层、车辆层、边界层不检测地面和环境装饰物这样能省下一半以上的检测开销。3. 强化学习状态空间、动作空间与奖励函数设计3.1 状态空间智能体怎么“看”世界状态空间的设计直接决定算法能不能学到有效策略。我给每辆自行车设计的状态分为三组自身状态、感知状态、目标状态。自身状态包括当前速度Vector3但实际使用时归一化到 0-1 范围、当前转向角、车身朝向偏差角。这里有一个关键处理所有输入到神经网络的数值必须归一化到一致的量纲范围否则数值较大的维度会主导梯度更新训练会异常缓慢甚至发散。比如速度如果直接传 5.2m/s 这个值而方向偏差是 0.05 这样的量级网络对速度维度的变化会更敏感对方向维度几乎不敏感。感知状态用射线传感器实现。我配置了 12 条射线均匀分布在智能体前方 180 度范围内检测距离设 20 米。每条射线输出两个值命中距离归一化为 0-1和命中物体的类型标签静态障碍物、动态车辆、红绿灯后者通过 One-Hot 编码传入网络。为什么不用视觉传感器视觉观测理论上信息更丰富但其需要 CNN 编码训练所需的样本量会大一个量级在自行车这种实时控制任务里性价比不如直接给精确的测距信息。目标状态是当前目标点的相对方位和距离。相对方位用智能体自身 forward 方向到目标方向的夹角距离归一化为 0-1。有了这两个值智能体就知道自己该往哪个方向走、还有多远。3.2 动作空间控制输出的选择动作空间我选择的是连续动作一个维度控制转向一个维度控制速度。转向输出范围是 [-1, 1]映射到实际转向角 [-25°, 25°]速度输出范围是 [-1, 1]映射到期望速度 [0, 6m/s]。为什么不把速度范围设计成带倒车真实自行车正常行驶不会倒车且在狭窄空间里加入倒车动作会让探索更加困难容易形成原地摇摆的局部最优。我最初加了倒车结果智能体学会了一套很有迷惑性的策略撞到障碍物前先倒车一点然后加速撞上去利用倒车缓冲来减少单帧碰撞惩罚烈度但整体碰撞次数根本没降。去掉倒车动作后这个投机取巧的行为自然消失了。另一个细节是动作平滑。直接让神经网络输出转向角并立即应用动作序列会非常抖实际表现为自行车蛇形前进。我在 Agent 脚本里加了一层低通滤波实际转向角 上一帧转向角 × 0.8 目标转向角 × 0.2相当于对动作做了平滑约束。这样做的好处是训练更容易收敛仿真里的运动也更接近真实自行车的行为特征。3.3 奖励函数引导策略收敛的关键奖励函数是强化学习系统里最考功底的部分它直接决定了智能体学出来是“乖孩子”还是“投机分子”。我最终采用的奖励构成如下到达目标点1.0稀疏奖励只在每次 episode 结束时给出与障碍物碰撞-0.5事件奖励碰撞瞬间触发闯红灯-0.3事件奖励在红灯状态下进入路口禁行区触发每步时间惩罚-0.01持续惩罚推动智能体走最短路径朝向目标点小奖励每帧距离目标点距离缩短时 0.001基于势函数的 reward shaping这里要特别强调时间惩罚和距离奖励的配合。如果没有时间惩罚智能体可以在原地停留来规避风险因为静止状态不会碰撞也不会违规最终学成一个“全程不动的佛系骑手”。如果时间惩罚太强比如 -0.05/步智能体会过度追求速度宁可碰撞也要冲碰撞惩罚根本压不住。我调了很多组参数最终锁定 -0.01 这个值平衡了效率和安全性。势函数奖励距离缩短 0.001这个设计要注意它必须和当前距离变化挂钩而不是单纯地“离目标近了就奖励”。否则智能体会学会一种钻空子的行为在原地绕圈因为某个角度上绕圈会导致投影距离变小于是不断刷奖励。我在实现时只计算沿道路方向上的距离变化绕圈属于横向位移不会产生正向奖励。3.4 PPO训练超参数与调优经验ML-Agents release 15 默认算法是 PPO配置文件用 YAML 描述。我最终使用的训练配置如下behaviors: BicycleAgent: trainer_type: ppo hyperparameters: batch_size: 2048 buffer_size: 20480 learning_rate: 3.0e-4 beta: 5.0e-3 epsilon: 0.2 lambd: 0.95 num_epoch: 3 learning_rate_schedule: linear network_settings: normalize: true hidden_units: 256 num_layers: 2 vis_encode_type: simple reward_signals: extrinsic: strength: 1.0 gamma: 0.99 max_steps: 5000000 time_horizon: 64 summary_freq: 10000这里有几个参数值得单独说明batch_size 和 buffer_size 的比例关系影响 PPO 更新的稳定性。2048 的 batch_size 配合 20480 的 buffer_size相当于每 10 个采样批次做一次策略更新这个比例在大多数连续控制任务里表现稳定。normalize 设为 true 非常关键。ML-Agents 会对输入观测做向量归一化这是个默认值得开启的配置项。我的第一版没开 normalize状态里速度和距离的量纲差异导致训练曲线一直在低位震荡开了之后奖励在 20 万步内就有明显上升。这个开关的成本几乎为零收益却很直接。max_steps 设 500 万步多智能体并行情况下10 辆自行车每步会产生 10 个环境交互样本实际跑完大约需要 50 万次环境步。在 Time Scale 拉到 20 时训练时长大约在 6 到 8 小时。如果没有 GPU 加速训练在 Python 端纯 CPU 也能跑时间会翻 2 到 3 倍。所以有条件的话用 GPU 训练能大幅缩短迭代周期。4. 训练提升与常见问题排查实录4.1 训练不收敛、震荡的表现与对策训练不收敛是最常见也最让人头大的问题。按照我的经验先看 TensorBoard 里的 Cumulative Reward 曲线如果曲线在某条平衡线附近来回震荡说明模型一直在探索但没有形成稳定策略。如果曲线缓慢下降说明奖励函数设计可能存在漏洞智能体在利用某个未被惩罚的边缘行为。我的第一版奖励函数里有个致命问题对红绿灯违规的惩罚只在进入 Trigger Zone 时才触发但智能体学会了从路口的对角线边缘擦过正好避开 Trigger Zone绕过惩罚。这个行为在训练曲线上表现为碰撞率下降但闯红灯率反而上升。排查方式是把验证场景里的智能体可视化逐帧观察轨迹才发现了这个利用几何缝隙的行为。解决这类问题没有银弹只能逐个检查。我后续加了一整套验证工具每个 Agent 的决策帧会同步生成一个 JSON 记录包含当前位置、速度、方向、目标距离、是否碰撞、是否违规训练完一版后就抓几段轨迹回放检查而不是只看奖励曲线这一个指标。4.2 物理仿真精度与训练速度的平衡Unity 的物理引擎默认 Fixed Timestep 是 0.02 秒50Hz这个精度对于自行车仿真来说是够用的。但如果为了追求更精细的碰撞响应把 Timestep 改到 0.005 秒物理精度到了训练速度会直线下降因为每个决策步需要执行 4 个物理子步。训练阶段我会把 Time.timeScale 设为 20这意味着仿真速度是实时的 20 倍10 万辆自行车在虚拟环境里同步跑训练效率大幅提升。但要注意timeScale 设置过高时物理引擎的子步计算会被压缩部分快速碰撞可能被隧道效应漏检表现为智能体偶尔穿模。这个问题在训练阶段可以容忍因为模型只是用来学策略不用来考究物理精确性。评估阶段我会把 timeScale 改为 1所有碰撞体检测恢复实时精度。如果你发现 timeScale 很高时训练效果明显变差优先检查是否是碰撞漏检导致的假阴性奖励。我遇到过一次timeScale 从 20 调到 50 之后碰撞惩罚的触发次数显著减少智能体开始学出随意穿行障碍物的策略。降回 20 后问题消失。所以 timeScale 不是越高越好要在训练效率和物理精度之间找到一个平衡点。4.3 多智能体训练的性能与碰撞难点多智能体场景下有两个典型问题样本相关性高和互相碰撞难收敛。样本相关性问题是因为多个智能体初始位置接近共享的初始状态相似导致经验池里的样本分布不均衡。我的解决办法是让每辆自行车的起点位置做随机扰动起点在赛道上的位置随机偏置 2 米目标点也在区间内随机生成这样经验池里的初始状态覆盖度大幅提升。互相碰撞难收敛的问题本质是每个智能体的行为会改变其他智能体的观测分布导致非平稳性。我在奖励里加了一个额外的群体惩罚项与其他自行车距离小于 1 米时每帧 -0.02。这个惩罚让智能体学会主动拉开间距避免抱团。实测在 8 辆自行车的场景里加入此惩罚后群体绕行成功率提升了约 30%。需要注意的是群体惩罚权重要控制好。如果过大智能体会过度保守所有自行车都保持很远距离导致道路利用率极低通行效率严重下降。从训练曲线看表现为平均速度持续走低。我最终把群体惩罚设为 -0.02单次碰撞 -0.5比例约为 125既能促成避让行为又不会太过保守。4.4 仿真环境到真实场景的落地差异思考仿真训练出的策略要搬到真实自行车或机器人上不可避免地会面临仿真与现实差异问题。Unity 里的物理参数和真实世界总有差距比如轮胎摩擦力、空气阻力、地面平整度这些参数在仿真里是常量真实环境下是变量。缓解这个问题的一个方向是域随机化。在训练阶段随机改变自行车质量、地面摩擦力、最大转向角等物理参数让策略学会在不同物理特性下都能保持基本性能。我在 release 15 版本里自己写了一个参数随机化脚本在每个新的 episode 开始时把刚体质量在 70-90kg 之间随机取值把地面物理材质里的动摩擦系数在 0.6-1.0 之间随机取值训练出的策略在不同参数环境下表现更稳定。另外姿态传感器的融合也是从仿真到实机的关键环节。仿真里智能体可以精确读取自身速度、朝向这是理想化的状态输入。真实环境需要通过编码器或惯性测量单元估计状态状态估计误差如果不纳入训练策略的鲁棒性会大打折扣。我后来在项目里加了状态噪声模拟层在观测值上加高斯噪声让智能体适应不完美的感知信息。5. 结果验证与效果评估5.1 训练曲线怎么看才靠谱训练曲线的判断不能只盯最后一条线要看三个阶段探索期、上升期、收敛期。探索期奖励在低位波动是正常的这个时候智能体在随机尝试各种动作进入上升期后奖励应持续走高偶尔回落但整体趋势向上收敛期奖励进入高位平台波动逐渐变小。我训练时观察到的曲线走势是0 到 20 万步奖励基本在 -100 到 -50 之间徘徊因为频繁碰撞导致大量负奖励20 万到 80 万步奖励快速拉升到 0 左右智能体开始会走直线、会绕大障碍物80 万到 300 万步奖励缓慢爬升到 20 左右智能体掌握了路口转向和动态避障300 万到 500 万步曲线进入平台期波动幅度显著减小。这里有个容易误判的点如果画的是平均累计奖励多智能体场景下不同智能体的表现方差很大平均奖励可能看起来一直在震荡即使整体策略已经不错了。所以我在 TensorBoard 里除了看均值还会单看前三个智能体的独立奖励曲线考察个体差异。5.2 场景泛化与压力测试训练完成后我最关心的不是训练场景里跑得多好而是没见过的场景里表现如何。为此我搭了两个泛化测试场景一个是地形和道路布局跟训练场景完全不同另一个是动态障碍物密度翻倍。泛化测试的结果显示策略在地形变化较大的场景里表现会打折扣但在交通标志和道路结构相似度较高的场景里迁移尚可。原因是射线感知是对局部信息的编码对全局布局不敏感而目标状态和信号状态提供了全局导航所需的核心信息。如果未来要做更大规模的跨场景泛化应该考虑加入语义地图或者拓扑导航的辅助输入。压力测试方面我把车道上同时存在的自行车数量从 8 辆增加到 16 辆观察碰撞率和通行效率。结果是碰撞率从 2% 上升到 7%通行效率下降约 18%但整体没有出现死锁情况所有智能体能保持运动状态。这个结果说明群体协调策略具备一定量的扩展性但超过一定密度后局部避障会让位于全局效率损失这个问题在真实交通里同样存在。5.3 后续扩展方向与实际部署建议仿真系统本身的扩展空间很大。一个方向是加入更复杂的交通参与者行为建模比如行人突然横穿马路、机动车突然变道这类非规则事件能有效测试和学习智能体的应对策略。另一个方向是引入多智能体个性化通过给不同智能体分配不同的行为参数或奖励权重让群体表现出快慢搭配、让行偏好等更接近真实骑手的行为风格。如果目标是往真实系统部署建议先做一个中间层把 Unity 仿真环境作为策略的验证场地在验证通过后再移植到嵌入式平台。移植时重点要解决模型规模与推理速度的平衡我的网络结构是两层 256 全连接ONNX 模型大约 1.2MB在 CPU 上推理一帧不到 3ms这个量级在 Jetson 或树莓派上都能跑起来不用担心实时性问题。如果你打算在仿真里继续深入可以考虑加一个东西把训练好的策略导出为 ONNX 后接入 Unity 的 Runtime Inference 模式让所有智能体在运行时直接推理而不需要连 Python 训练器。这个模式下系统变成一个独立的可执行程序非常适合做演示、赛事或者产品原型。ML-Agents release 15 原生支持这个流程只需要在脚本里引用Unity.ML-Agents.Extensions的 Runtime 命名空间加载模型后走Agent的决策接口。最后再分享一个项目管理的经验训练任务一定要做成可重复、可配置的方式所有场景参数、奖励权重、动作范围都放在配置文件里不要写死在代码里。我因为偷懒把奖励权重写死在脚本里后来调整参数时要把整个训练重跑一遍白白浪费了十多个小时的训练时间。把这些参数外置成 JSON 或 YAML 配置每次调参只需要改配置文件就能重新出结果这对迭代效率的提升是决定性的。本文还有配套的精品资源点击获取
返回列表