我先说一下结论SMARTS 是华为诺亚方舟实验室开源的智能交通/自动驾驶多智能体仿真平台全称很长但你只要记住它是一个专门为“多智能体强化学习”设计的训练场就行。它跟 CARLA、SUMO 这些仿真器最大的区别在于多智能体强化学习不是它后来硬凑的功能而是平台一开始就围绕它来设计的。PPOProximal Policy Optimization近端策略优化又是强化学习里最常用、最稳的 baseline 算法之一所以“SMARTS PPO”这个组合本质上就是在用一套标准配置把整个仿真环境、环境封装、奖励设计、训练流程全部验证一遍。这篇文章适合两类人一类是刚开始接触 SMARTS、想尽快把自己手里的场景跑起来的研究生或者工程师另一类是熟悉强化学习但对仿真平台还比较陌生想知道怎么把 SMARTS 包装成 gym 环境、再交给 PPO 去训练的人。我不会花太多篇幅推导 PPO 的数学公式重心放在“如何把环境搭好、把 baseline 真正跑起来”原理只挑影响调参的关键点讲。1. 项目总览与运行思路1.1 SMARTS 平台能做什么为什么要用 PPO 做 baselineSMARTS 可以帮你搭出带真实路网结构、信号灯、车流和行人交互的交通场景然后在里面同时训练和评估多个智能体。它提供了比较完整的传感器观测接口比如距离传感器、前方车辆信息、车道几何信息等也支持用 SUMO 做底层交通流仿真。换句话说你在里面做自动驾驶决策、车路协同、路口通行策略这些研究基础设施都是现成的。但“现成”不意味着“零成本”。SMARTS 的工程复杂度一直不低安装依赖多、场景资产需要单独下载、环境注册名还经常随着版本变化。如果你一上来就跑很复杂的多智能体场景出问题之后根本分不清是环境问题还是算法问题。这时候 PPO 的价值就体现出来了PPO 是公认的“下限很高”的算法训练稳定、对超参数不敏感大部分新任务用默认参数都能跑出一个说得过去的结果。所以业内习惯是拿到一个新环境先用 PPO 跑通一遍确认观测空间、奖励设计、动作接口都没有问题再往上换更复杂、更激进的算法。我这里用的组合是 stable-baselines3 的 PPO 实现配合 SMARTS 暴露出来的 gym 接口。SMARTS 仓库里有些版本自带 PPO 示例有些版本只带 RLlib 示例但不管哪种核心链路都是一样的创建场景、封装环境、实例化 PPO、开始训练。把这套链路跑通你后面换任何算法、换任何场景都是在同一个框架下面做替换。1.2 整体运行链路拆解把一个 SMARTS PPO baseline 跑通大概要经过下面这几步准备系统环境装好 Python、conda、C 编译工具链、SUMO 仿真器。安装 SMARTS 本体拉源码、装依赖、验证导入。准备场景资产下载路网、地图、车流数据或者先用内置的小 demo 场景。找到或编写 PPO 训练脚本把场景包装成 gym 环境再用 stable-baselines3 或 RLlib 加载。启动训练并观察确认奖励曲线是上升的模型能正常保存。排查问题环境冲突、路径不对、渲染报错、训练发散。这套链路看起来不复杂但你会发现真正耗时的是前两步。SMARTS 不像普通 Python 包那样pip install完就能直接跑它依赖 SUMO 做路网仿真依赖一批 C 库做场景构建还依赖一个叫 Envision 的可视化服务。这些系统级依赖如果装不对后面报错信息往往很隐晦新手很容易卡住。2. 环境准备与依赖安装2.1 硬件与系统前置要求先给一个最低配置和推荐配置。最低配置是 CPU 能跑、内存 16GBUbuntu 20.04 或 22.04Python 3.8 到 3.10并且需要有 sudo 权限来安装系统依赖包。推荐配置是 CPU 8 核以上、内存 32GB如果有 NVIDIA GPU 会更好因为 SMARTS 仿真本身不吃 GPU但 PPO 训练过程中如果场景里智能体多、观测维度大GPU 能明显加快网络更新速度。我强烈建议你在 Linux 上安装Ubuntu 或者 Debian 系最好。SMARTS 虽然也支持 macOS但不少系统依赖的安装路径差异很大出了问题网上能查到的资料也少。Windows 用户就别强求了直接上虚拟机或者 WSL2。我在 WSL2 下跑过能用但要注意 WSL2 的显卡透传和 GUI 支持后面会提到怎么绕开图形界面的坑。注意无论你是用 Ubuntu 还是 WSL2先确认 shell 能正常执行 bash 脚本因为 SMARTS 仓库里的安装脚本是 bash 写的如果路径里有空格或者权限不对脚本很容易在某个不起眼的地方挂掉。2.2 Python 环境与 conda 配置装 SMARTS 的第一原则是千万别往系统 Python 里装。SMARTS 依赖的 protobuf、gym、numpy、pybind11 这些包很容易跟你本来的深度学习环境打架而且你后面做实验大概率还要切不同版本所以一定要建一个独立的 conda 环境。这一步看起来多此一举但能帮你省下大量“装完又崩、崩完又装”的时间。conda create -n smarts python3.8 conda activate smarts为什么推荐 Python 3.8因为 SMARTS 官方长期在 3.8/3.9 下测试得最多很多老版本的 pybind、SUMO 绑定在 3.10 上也能跑但遇到问题后排查成本更高。如果你手头只有 3.10也不用焦虑大概率没问题只是碰到编译类报错时多留个心眼。建完环境后顺手升级一下基础工具pip install --upgrade pip setuptools wheel这三个工具不升级后面安装某些带 C 扩展的依赖时可能会出现非常莫名其妙的编译报错。这类问题跟代码本身无关纯粹是工具链太老导致的。2.3 安装 SUMO 与系统级依赖SMARTS 的交通流仿真依赖 SUMOSimulation of Urban MObility所以这一步是关键。在 Ubuntu 上最省事的办法是用官方 PPA 安装sudo add-apt-repository ppa:sumo/stable sudo apt-get update sudo apt-get install sumo sumo-tools sumo-doc装完之后一定要配置环境变量并确认 SUMO 能正常执行export SUMO_HOME/usr/share/sumo export PATH$PATH:/usr/bin/sumo sumo --versionSUMO_HOME这个环境变量如果没配好SMARTS 在构建交通场景时会报“could not locate sumo”一类的错误而且它不会在启动时就报往往是你训练跑了几百步之后才崩排查起来特别折磨。所以我建议把上面三行写进~/.bashrc确保每次打开终端都在。除了 SUMOSMARTS 编译本体时一般还需要这些 C 库sudo apt-get install libspdlog-dev libgflags-dev libgoogle-glog-dev libfmt-dev不同 SMARTS 版本对这些库的版本要求略有差异。如果你 clone 的版本比较新优先看仓库里的 README 和scripts/setup/setup.sh里面通常写明了推荐安装方式。这里有一个常见的坑如果你的系统源里这些库版本偏低编译 SMARTS 时会出现Undefined symbols之类的链接错误。解决方法不是手工升级系统库而是先确认你 clone 的分支和仓库要求的 Ubuntu 版本一致再重新编译。3. SMARTS 安装与资源准备3.1 源码安装 SMARTSSMARTS 的安装方式有两种一种是用 pip 直接安装发布包另一种是从源码安装。虽然 pip 安装快但如果你要改环境、看示例代码、调试 baseline源码安装明显更方便而且能保证版本一致性。我建议直接用源码方式。先把仓库拉下来git clone https://github.com/huawei-noah/SMARTS.git cd SMARTS然后激活之前创建的环境执行源码安装conda activate smarts pip install -e .这里有个细节pip install -e .会读取setup.py把 SMARTS 的所有 Python 依赖都装进当前环境同时以开发模式注册这个包。之后你改 SMARTS 的 Python 源码会直接生效不用反复安装。如果仓库里带了./scripts/setup/setup.sh这类一键安装脚本我建议你先完整读一遍这个脚本再决定要不要跑。它会把很多系统依赖和编译步骤自动化但里面可能有sudo apt-get操作你得确保当前用户有 sudo 权限。执行脚本时如果中途报错不要一上来就怀疑脚本本身把日志往上翻找到第一个失败的依赖通常单独安装那个包就能解决。3.2 场景与地图资源准备SMARTS 仓库本身不会附带太多场景数据很多 demo 用的地图、路网、车流数据需要单独下载。你可以在仓库里找datasets目录或者看scripts/setup下有没有类似download_scenarios.sh的下载脚本。如果没有去 GitHub Release 或相关数据页面找对应版本的数据包。不过在你下载大批真实路网数据之前我建议先用仓库内置的小 demo 场景把链路跑通。SMARTS 的scenarios目录下通常会内置几个简单的路网比如intersections/4way这类直接拿这些场景跑 baseline可以最大程度减少“场景文件缺失”这个变量。下载下来的场景数据建议统一放到一个固定目录比如~/smarts/datasets然后在训练脚本里通过绝对路径引用。这样有个好处你不会因为不同脚本的相对路径不同而反复拷贝场景文件也不会因为工作目录切换导致找不到数据。3.3 验证安装是否成功这一步值得认真做因为很多问题在后面暴露时你很难判断到底是前面装坏了还是当前脚本的问题。先验证 Python 能不能正常导入python -c import smarts; print(smarts.__version__)如果能打印出版本号说明 SMARTS 主包已经装好。接下来验证仿真核心能不能启动可以跑一个最简单的示例脚本。仓库里如果带了examples/ego_open_loop这类轻量示例直接运行它。只要不报错、能打出几行仿真日志就说明核心组件没问题。这时候再进训练阶段你的问题面会小很多。4. PPO baseline 代码概览与关键配置4.1 代码结构与你可能需要自己补的部分SMARTS 仓库里的示例代码一般放在examples或者baselines目录下不同版本结构不太一样。比较常见的是examples/rllib下有 RLlib 的训练入口有些版本会单独放一个 PPO 示例目录。如果仓库里自带 PPO 脚本那你直接配好参数就能跑如果没有自己写一个训练脚本也就几十行核心流程分三步创建 gym 环境、包装成 stable-baselines3 能用的格式、实例化 PPO 并训练。我这里给一个最小示例以 stable-baselines3 为例import gym from stable_baselines3 import PPO from stable_baselines3.common.vec_env import DummyVecEnv from smarts.env.gym.wrappers.single_agent import SingleAgent SCENARIO scenarios/intersections/4way def make_env(): env gym.make( smarts.env:hiway-v0, scenarioSCENARIO, headlessTrue, sumo_headlessTrue, ) return SingleAgent(env) env DummyVecEnv([make_env]) model PPO( MlpPolicy, env, learning_rate3e-4, n_steps2048, batch_size64, n_epochs10, gamma0.99, gae_lambda0.95, clip_range0.2, ent_coef0.005, verbose1, tensorboard_log./logs/ppo, ) model.learn(total_timesteps200_000) model.save(ppo_smarts_model)这个脚本里有几个点需要展开。smarts.env:hiway-v0是 SMARTS 对外暴露的 gym 环境 id但不同版本的注册 id 可能不一样有可能是SMARTS_ENV或者hiway-v1。如果你导入时报gym.error.UnregisteredEnv十有八九是环境注册名对不上直接去smarts/env目录下看注册的 entry point 就知道了。SingleAgent这个包装器很关键。SMARTS 底层是多智能体环境但很多 PPO baseline 默认是单智能体策略用它包一层之后环境会把第一个需要控制的车辆转换成标准单智能体接口。如果你要训练多智能体协作策略那就不能只靠这个包装器得用 RLlib 或 stable-baselines3 的多智能体扩展来处理。4.2 PPO 超参数配置要点上面代码里的 PPO 超参不是随便写的这是一套在连续控制、自动驾驶类任务里很通用的起点配置。learning_rate3e-4是很多 PPO 开源实现的默认值稳定性好不容易出现单次更新太大把策略改崩的情况。n_steps2048表示每个环境每轮收集多少步经验后再统一做更新它决定了经验收集和梯度更新的频率。batch_size64是每次梯度更新的 mini-batch 大小注意它最好能被n_steps整除。clip_range0.2是 PPO 的裁剪范围也是算法里 “Proximal” 的核心体现它限制了新旧策略的更新幅度。自动驾驶场景里这个值通常保持 0.2 附近就够用。ent_coef0.005是熵正则项的系数作用是鼓励探索如果训练时发现智能体一直停在原地可以适当调大一点。如果你用的是 RLlib 版本对应 PPO 配置也差不多只是字典里的键名不一样比如train_batch_size、sgd_minibatch_size、clip_param等。含义和上面一致迁移不困难。4.3 reward 设计与场景选择baseline 能跑通不代表能学到有意义的行为。SMARTS 不同场景、不同 reward 函数出来的训练曲线差异非常大。我第一次跑的时候把 reward 设成“每步给一个小的正数、碰撞给大惩罚”结果智能体学到的是原地不动因为不动也能一直拿正数还能规避碰撞惩罚。后来改成“鼓励前进速度、惩罚碰撞和长时间停留”的组合 reward行为才正常。所以我的建议是在跑任何 baseline 之前先读一下场景自带的 reward 逻辑或者自己定义一个 reward wrapper。一个简单但合理的公式是前进速度接近目标速度给连续的正奖励碰撞其他车辆或路沿给一个较大的负奖励长时间停留在原地给一个小惩罚到达目标区域给额外正奖励。注意 reward 的数值尺度不能太悬殊。如果碰撞惩罚是 -1000而前进奖励只有 0.01这种差距会让训练极其不稳定PPO 做归一化时也会被极端值带偏。5. 运行 PPO baseline 实战5.1 命令行启动训练确认环境、代码都没问题之后启动训练就比较简单了。如果仓库自带训练脚本通常会提供类似这样的参数接口python examples/ppo_baseline/train.py \ --scenario scenarios/intersections/4way \ --total-timesteps 200000 \ --seed 0 \ --log-dir ./logs如果用自己写的最小脚本直接运行python train_ppo_smarts.py第一次运行时会初始化 SUMO 仿真、加载路网屏幕会打出大量日志。这时候重点观察几个信号日志里有没有出现Starting scenario、Loading map等信息有没有“重置环境”“场景开始”之类的输出有没有立刻报错。如果日志停在同一行超过几十秒不动多半是场景资源加载不出来或者 SUMO 启动超时先用CtrlC中断再去检查路径。训练开始后终端会周期性打印训练进度。stable-baselines3 在verbose1时会打印平均 episode 长度、平均 reward 和 loss。你主要盯平均 reward只要不是持续负值或者完全不动就是正常的。PPO 本身训练曲线波动大偶尔一次下降不代表有问题。5.2 训练过程可视化与模型保存用 TensorBoard 看曲线是很高效的方式tensorboard --logdir ./logs/ppo浏览器打开localhost:6006重点看三个指标rollout/ep_rew_mean平均回合奖励、train/policy_gradient_loss策略梯度损失、train/value_loss价值函数损失。如果 reward 逐步上升说明策略在学东西如果 reward 在一个平台上横着不动也不要太着急PPO 在复杂场景、稀疏奖励下经常阶段性爬升。模型保存方面我喜欢同时保存两种格式一种是 SB3 的 zip 包方便继续训练另一种是 ONNX方便后续部署。SB3 保存和继续训练都非常简单model.save(ppo_smarts_final) # 继续训练 model PPO.load(ppo_smarts_final) model.set_env(env) model.learn(total_timesteps100_000)ONNX 导出需要先加载 policy 再转 torch jit最后转 onnx流程稍微多一点我准备后面单独写一篇部署教程这里先不展开。5.3 可视化界面 Envision 与 headless 模式SMARTS 自带一个叫 Envision 的可视化 Web 界面。训练时如果用headlessFalse或者传入envisionTrue它会启动一个 HTTP 服务浏览器访问localhost:8081就能看到路网、车辆、信号灯的实时状态。这个对调试非常有帮助你能直观看到自己的智能体是在往前走还是在原地打转。但是在服务器上跑训练时往往没有显示器这时候务必用 headless 模式headlessTrue、sumo_headlessTrue。我踩过一次坑服务器上没开 X 服务结果训练脚本一启动就报 GUI 初始化失败。解决方法是把所有渲染相关参数都明确设成 headless。如果你想去远程看画面可以单独做端口转发来访问 Envision但平时训练就用 headless省资源也稳定。还有一个容易被忽视的点headless 模式下 SUMO 仍然可以在后台跑交通流仿真只是没有渲染窗口所以训练效率反而更高。你可以在同一个终端里开两个进程一个训练、一个起 Envision 做观测只要把端口避开就行。6. 常见问题与排查实录6.1 安装类问题速查现象常见原因解决办法clone 后找不到示例目录或场景文件clone 到 release tag 或分支不对检查当前分支切到 main 或指定 tagpip install -e .时报 C 编译错误系统缺少编译依赖安装 libspdlog-dev 等依赖确认 gcc 版本import smarts报模块找不到没激活 conda 环境或没有执行源码安装conda activate smarts后重新执行pip install -e .UnregisteredEnv: smarts.env:hiway-v0环境注册名和版本不匹配查看smarts/env/__init__.py使用正确的 idSUMO 相关报错SUMO 路径或环境变量未配置设置SUMO_HOME检查sumo --version这张表里出现率最高的就是环境注册名不匹配的问题尤其是不同 SMARTS 版本切换时这个错误特别容易遇到。6.2 运行阶段问题实录训练时最常见的现象是“一启动就崩”和“跑到一半卡死”。一启动就崩大概率是场景路径写错了。比如你写了scenarios/intersections/4way但实际目录结构是scenarios/intersections/4way_1SMARTS 加载地图文件时找不到就会直接抛异常。解决方法是先看示例自带配置用的场景路径照抄确认。跑到一半卡死通常是 SUMO 线程和 Python 线程之间的死锁。SMARTS 内部有自己的一套线程管理但如果你在自定义脚本里频繁重置环境或者使用多个并行 env就很容易触发这种问题。解决办法是先把并行环境数降到 1确认单环境稳定没问题之后再加并行。我自己实验下来很多“随机卡死”其实就是并行开太多导致的。还有一类问题是 gym 版本冲突。stable-baselines3 对 gym 的版本很敏感某些版本要求 gym 0.21 或者特定的 gymnasium 版本而 SMARTS 可能依赖另一个版本。这种问题会表现为“导入 SB3 正常导入 SMARTS 也正常但合在一起就报某个属性不存在”。我的建议是创建一个干净的 conda 环境先装 SMARTS 再装 stable-baselines3如果发生冲突就查看两个库的 requirements 文件找一个它们都兼容的版本区间。6.3 训练阶段问题实录奖励不涨怎么办如果训练能跑但奖励曲线一直很差这里有一个我总结的排查顺序先看奖励尺度。统计一下前 1000 步的平均 reward如果是负的想想能不能改成“向前开就给正奖励”的设定。这个改动往往比调任何算法参数都有效。再看观测空间。SMARTS 默认的观测空间包含很多传感器数据如果维度很大策略网络一开始很难收敛。可以把观测降到核心维度比如只保留自车位置、速度、下一路口位置、最近车辆距离。最后看动作空间。SMARTS 支持连续动作油门、刹车、转向和离散动作换道、加减速。PPO 两种都能处理但离散动作空间小训练速度快连续动作更接近真实车辆控制但需要更长训练时间和更大的网络。我的建议是 baseline 阶段先用离散动作空间把整个流程跑通等确认奖励、环境都没问题再切到连续动作空间。这个顺序能帮你节省大量调试时间。7. 经验心得与后续扩展建议7.1 三条最具性价比的实践经验第一独立 conda 环境是底线。SMARTS 依赖的包很重尤其 protobuf 和 gym 这两个跟很多深度学习项目都有冲突。我见过太多人把 SMARTS 直接装在 base 环境里结果跑别的项目时被搞得焦头烂额。单独建环境这个习惯能让你少受很多罪。第二headless 模式应该成为默认。即使你的电脑有显示器我也建议先用 headless 模式跑通再开 Envision 看可视化。因为 Envision 的刷新会占用一定 CPU而且如果你同时在调多个实验开多个可视化界面会非常吃内存。训练归训练可视化归可视化。第三小场景验证永远比大场景快。不要一上来就下载一堆真实路网数据。先用4way这类内置小场景把环境、算法链路跑通再逐步换到复杂场景。这个原则不仅适用于 SMARTS也适用于任何强化学习项目。7.2 从 baseline 走向多智能体与部署跑通单智能体 PPO baseline 之后下一步建议这样做先把场景换成带车流、信号灯的复杂路网体验真实交通环境里的训练难度然后尝试多智能体 PPO用 RLlib 训练多个能协作的车辆比如让车队在匝道汇入时互相让行最后再考虑模型部署把训练好的策略导出成 ONNX接入到仿真平台里做闭环测试。SMARTS PPO 这套组合最大的学习价值其实不在 PPO 本身而在它逼着你把仿真环境、接口封装、数据流全部搞清楚。刚开始跑不过去时会觉得这平台怎么这么折腾但环境搞定之后后面做实验的收益是实打实的。最后说一个我的个人习惯每次跑实验之前我都会记一下当前代码用的 SMARTS commit 版本和 Python 依赖版本因为这类仿真平台迭代太快同一个脚本在不同版本下行为可能完全不一样。这个记录习惯在后续复现实验时能救你很多次。