做自动驾驶规划控制的人,早晚会撞上一个很尴尬的场面:算法在自己搭的仿真里跑得漂漂亮亮,换一份别人给的场景数据立刻原形毕露。问题往往不在算法本身,而在数据——地图格式不一样、障碍物表示不一样、时间步长不一样、坐标系定义不一样,你的轨迹在别人的数据上根本没法复现,更别提横向对比谁的规划器更好。CommonRoad就是冲着这个痛点来的:它用一套统一的 XML 格式把交通场景固定下来,再配上一整套读取、可视化、转换、评测的 Python 工具链,让不同团队做出来的规划器能在同一批场景上跑同一条赛道。这篇内容我会把 CommonRoad 是什么、由哪些部分组成、安装过程中每一步为什么这么做讲透,最后给出一套能直接抄的安装与验证流程,以及我踩过的那些坑。不管你是刚入门自动驾驶的学生,还是需要给 ADAS 功能做场景化验证的工程师,都能从里面拿到能用的东西。
需要先说清楚一点:CommonRoad 不是仿真器,它不做物理引擎那套碰撞、轮胎、悬架的计算,它管的是"场景"这件事——场景怎么描述、怎么读、怎么画、怎么在不同格式之间倒腾、怎么用它来打分。搞清楚这个定位,后面安装哪几个包、装完之后能干什么,逻辑就顺了。
1. CommonRoad 到底是个什么东西
1.1 从"仿真里跑得通,车上不敢跑"这个老问题说起
做规划控制验证,最贵的是实车路测,最不靠谱的是纯随机仿真,最有用的是带真实交互的场景回放。可现实是,场景数据的来源极度分散:有的是从自然驾驶数据集里切出来的片段,有的是从交通仿真软件里导出的路网,有的是人工照着事故报告手绘的。这三类数据各说各话,前者的坐标系可能是经纬度,后者可能是局部笛卡尔,时间步长从 0.01 秒到 1 秒都有。你想把三份数据放到同一个评测脚本里跑,光是写格式转换就够喝一壶。
CommonRoad 的做法是把场景抽象成一个与具体仿真器解耦的中间层。一个场景文件描述的是"某一时刻,某条路上有哪些车道、哪些车、哪些交通标志,其中某一辆车的初始状态是什么、要去哪里"。这个描述是纯几何和纯运动学的,不绑定任何动力学模型。于是你用什么车辆模型去跑这条轨迹,是你的自由;而场景本身的描述,是所有人共享的。
它的核心价值可以归成三条。第一,统一的场景描述格式,让数据可以跨团队流转;第二,完整的工具链,读、写、画、转、评测都有现成轮子,不用自己造;第三,公开的基准与场景集,你的算法跑出来的指标能跟别人比。这三条里,第一条是地基,后两条是让它真正被学术界和工业界用起来的原因。
1.2 场景格式、工具链、基准评测,三件套各管什么
很多人第一次接触会被一堆仓库名绕晕,其实按功能分层看就很清楚。
场景层是数据本身。一个 CommonRoad 场景文件是 XML,里面主要有几块内容:车道网络(lanelet network,用左右边界点列描述每条车道,并记录前后继、左右相邻关系)、静态障碍物(比如停在路边的车、隔离墩)、动态障碍物(带初始状态和轨迹的其他车辆)、交通标志与信号灯、以及规划问题(planning problem,包含自车的初始状态和一个目标区域)。你拿着这个文件,就能完整复现一个交通片段。
工具层是围绕这个格式的一堆 Python 包。最核心的是commonroad-io,负责读写、可视化、坐标变换、场景修补;commonroad-scenario-designer负责生成和转换场景,能把 SUMO 路网、OpenDRIVE、Lanelet2 等格式互转;commonroad-vehicle-models提供几种常用的车辆运动学/动力学模型,比如单轨模型、点质量模型;commonroad-route-planner做路径和路线规划;commonroad-drivability-checker用一个 C++ 后端高效判断某个位姿是否落在可行驶区域内,这是做碰撞和越界检查的关键;再往上还有commonroad-reach(可达集计算)、commonroad-crime(临界性度量,用来量化场景有多"危险")、以及和各种仿真器的桥接包。
基准层是评测。CommonRoad 会维护若干场景集,按难度和场景类型分类,比如手绘场景、车辆模型相关场景、交互密集场景等,并提供统一的评测脚本和指标。这部分的意义在于,你不需要自己定义"什么叫规划得好",照着官方指标跑就行。
提示:如果你只是想快速上手看看效果,装
commonroad-io加一个场景文件就够了,其他包属于按需索取。一上来就把所有仓库 clone 下来,大概率会在依赖编译上浪费一整天。
1.3 什么人适合花时间啃它
说句实在话,CommonRoad 不是给所有人准备的。它对你有没有用,取决于你在链条上的位置。
如果你在做轨迹规划或运动规划的算法研究,那它几乎是绕不开的:论文里常见的做法就是在 CommonRoad 场景集上跑,然后比成功率、舒适度、计算耗时。你不装它,就没法跟别人对话。
如果你在做ADAS 或自动驾驶功能验证,需要拿一批典型场景去压测自己的控制器,那它可以当你的场景数据源和回放框架。它的场景是几何级别描述,不绑定动力学,正好适合你把自家的车辆模型塞进去跑。
如果你在做场景生成或者数据集处理,commonroad-scenario-designer那套格式转换能力比你自己写解析器靠谱得多,尤其是 OpenDRIVE 和 Lanelet2 这类格式,自己解析一遍就是几个月的坑。
如果你只是想入门自动驾驶、找个能跑起来的小项目练手,它也算合适,因为安装门槛不算高,而且跑通加载和可视化只要几十行代码,反馈很直观。但要注意,它不教你怎么写规划器,它只给你数据和工具。
2. 装之前先把环境理清楚
2.1 Python 版本怎么选,为什么不能随手装个最新的
这是我在这个环节见过最多的翻车点:有人图省事,直接去官网下了最新版 Python,装完pip install commonroad-io一路报错,然后怀疑人生。
原因不复杂。CommonRoad 的依赖里有一批是带 C 扩展的包,比如numpy、shapely、lxml,这些包在 PyPI 上分发的是预编译好的 wheel(也就是已经编译好的二进制包)。某个特定版本组合下,官方才会为某个 Python 版本提供对应平台的 wheel。你装了一个太新的 Python,wheel 还没跟上,pip 就只能退回去下源码包自己编译。而在 Windows 上编译这些 C 扩展,需要装 Visual C++ 构建工具,一编译就是十几分钟,还随时可能因为缺个编译标志失败。
我的建议是用 3.9 或 3.10,这两个版本在科学计算生态里的 wheel 覆盖最完整,踩坑概率最低。如果你手头项目对版本没强约束,就选这两个,不要逞强上新版本。装的时候顺手把 pip 升级到较新版本,老版本 pip 在解析依赖时容易出现"解析得很慢或者解出错误组合"的问题。
python --version # 输出确认在 3.9.x 或 3.10.x python -m pip install --upgrade pip setuptools wheel再有一条经验:别用系统自带的 Python。macOS 自带的、Linux 发行版自带的 Python 往往跟系统组件绑定,你往里面装包,轻则污染,重则搞坏系统工具。要么用 pyenv、要么用 conda、要么老老实实建虚拟环境。
2.2 底层依赖到底有哪些,谁在拖后腿
commonroad-io自身的依赖不算夸张,但每个都有明确用途,理解它们能帮你在报错时快速定位。
| 依赖包 | 主要作用 | 常见坑点 |
|---|---|---|
| numpy | 所有点列、状态向量的底层数组 | 版本过高时与其他包不兼容 |
| scipy | 插值、积分、空间变换 | 通常跟随 numpy 版本联动 |
| matplotlib | 场景可视化渲染 | 后端配置不对会直接崩在 import |
| lxml | 解析 CommonRoad XML | 需要 libxml2,容器里常缺 |
| shapely | 几何计算,如点在多边形内 | 2.x 与 1.x API 有破坏性变更 |
| networkx | 车道拓扑图的路径搜索 | 大场景下内存占用明显 |
| pillow | 图像导出 | 一般无坑 |
| commonroad-vehicle-models | 车辆模型 | 与 io 版本需匹配 |
重点说两个。matplotlib的坑在于,如果你在无图形界面的服务器上跑代码,默认后端(有些版本是 TkAgg 之类)会因为找不到显示设备而报错。解决办法是在 import pyplot 之前先设置非交互后端:
import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt这样绘图只出文件不弹窗,适合在服务器或者 Docker 里批处理。shapely的坑在于2.0 版本做了一轮 API 清理,一些老代码里的调用方式被移除,如果你同时装了别的依赖老版本 shapely 的包,pip 会陷入两难,这时候手动锁版本比让它自己解析更快。
2.3 虚拟环境:venv 和 conda 的选择与实操
虚拟环境这一步,我建议不要省。理由很简单:CommonRoad 对科学计算包的版本有要求,你机器上如果已经装了另一个项目的 numpy,两者很容易打架。虚拟环境把依赖隔离在项目目录里,出问题直接删掉重来,成本极低。
用标准库自带的 venv,最轻量:
# 新建环境 python -m venv .venv # 激活(Linux / macOS) source .venv/bin/activate # 激活(Windows PowerShell) .venv\Scripts\Activate.ps1 # 激活成功后再升级 pip python -m pip install --upgrade pip如果你机器上已经有 Anaconda 或 Miniconda,用 conda 建环境也行,好处是对非 Python 的底层库(比如 C++ 编译工具链)管理得更方便:
conda create -n commonroad python=3.10 -y conda activate commonroad python -m pip install --upgrade pip选哪个?我给个实用判断:只在 Python 层面折腾,用 venv;需要摆弄编译器、C++ 依赖、或者你已经习惯 conda 生态,用 conda。两者别混用,在 conda 环境里再用 venv 套一层,出问题时会很难查。
注意:Windows 上用 PowerShell 激活脚本时,如果提示"禁止运行脚本",是执行策略的限制,改一下当前用户的策略即可,不要动全局策略。这一步网上教程很多,不展开。
3. 三种安装路径实操
3.1 pip 安装:最省事的路线
对绝大多数人来说,这条路就够了。
# 确保在虚拟环境里 pip install commonroad-io国内网络环境下,如果下载速度慢或者频繁超时,可以指定镜像源:
pip install commonroad-io -i https://pypi.tuna.tsinghua.edu.cn/simple装完之后立刻验证,不要等写完代码才发现没装上:
python -c "import commonroad; print(commonroad.__version__)"能打印出版本号,说明包本身没问题。这里有个小坑:有人执行这条命令报ModuleNotFoundError,第一反应是重装,其实十有八九是装到了另一个 Python 里。检查方法是:
which python # Linux / macOS where python # Windows which pip如果python和pip指向的路径不在同一个环境目录下,那就是环境错位。用python -m pip install ...代替裸pip install ...基本能规避这个问题,因为它是明确让当前这个 Python 去执行 pip。
如果你还需要生成和转换场景,再补一个:
pip install commonroad-scenario-designer车辆模型包通常会被commonroad-io作为依赖自动装上,没装上就单独补。
3.2 源码安装:需要改代码或者跟仓库最新进展时用
什么时候该走源码?两种情况:一是你要读源码、改源码,或者给项目提 issue 需要复现最新代码的行为;二是 pip 上的发布版有 bug,而修复合在仓库里。
前提是机器上有 git。Linux 下一条命令搞定,Windows 下装完记得配置用户名和邮箱,否则提交时会报错:
git config --global user.name "your-name" git config --global user.email "your-email@example.com" git config --global core.autocrlf input最后那条core.autocrlf input是给跨平台协作用的,避免 Windows 的换行符被提交进仓库导致整文件 diff。
然后克隆并安装:
git clone <commonroad-io 的仓库地址> cd commonroad-io pip install -e .这里的-e是关键,表示"可编辑安装"。它不会把包复制到 site-packages,而是建一个链接指回你的源码目录。你改了源码,直接生效,不用重装。反过来,如果你不加-e,改完代码发现没生效,会怀疑人生。
我个人更推荐用 pip 装发布版做日常使用,另开一个源码目录做实验,两个环境分开。混在一起最容易出现的状况是:你为了调试改了一行,后来忘了改回来,结果跑出来的结果和预期不符,排查半天才发现是代码被动了。
3.3 可行驶性检查器的编译,难点基本都在这
commonroad-drivability-checker是这套工具里最不好装的一个,因为它带 C++ 后端。它做的事情很实际:给定车辆形状和一个位姿,快速判断这个位姿在不在可行驶区域内,做批量检查时性能比纯 Python 版本高一个量级。
安装方式通常是先用 pip 试一下:
pip install commonroad-drivability-checker如果官方为你的 Python 版本和目标平台提供了预编译包,这一步会直接成功,那你运气不错。如果没有,就会进入源码编译流程,需要:
- 一个能用的 C++ 编译器(Linux 上是 g++,Windows 上是 MSVC)
- CMake,用来生成构建文件
- Eigen,做线性代数运算
- Boost,提供一些基础组件
Linux 上用包管理器装前置依赖大致是这样:
sudo apt update sudo apt install -y build-essential cmake libeigen3-dev libboost-all-dev然后按仓库 README 的说明编译安装。这里我不写死具体命令,因为不同版本的构建脚本差异不小,以仓库文档为准比自己猜命令靠谱。我要强调的是三个高频失败原因。
第一,编译器版本太老。一些 C++ 特性需要较新的 g++,系统的默认版本不够就得手动指定。第二,Eigen 找不到。CMake 报错说找不到 Eigen3 是很常见的,通常是因为头文件路径没被自动发现,需要在 CMake 配置阶段手动指定。第三,Python 版本不匹配。编译产物和 Python 解释器是绑定的,你在 3.10 环境里编译的,拿到 3.9 里用不了。所以务必在激活了正确虚拟环境的前提下编译。
提示:如果你的目的只是跑通场景加载和可视化,完全可以先跳过这个包。等真正需要做精确的碰撞和越界检查时再回头啃它,不要让它卡住你的整个上手流程。
4. 验证:把第一个场景加载出来
4.1 先看懂场景文件长什么样
在写代码之前,花十分钟看一眼 XML 结构,后面报错时你会感谢自己。
一个 CommonRoad 场景文件大致长这样(下面是简化示意,实际字段更全):
<?xml version="1.0" encoding="utf-8"?> <scenario> <scenarioTags> <country>DEU</country> </scenarioTags> <lanelet id="1"> <leftBound> <point><x>0.0</x><y>0.0</y></point> <point><x>10.0</x><y>0.0</y></point> </leftBound> <rightBound> <point><x>0.0</x><y>-3.5</y></point> <point><x>10.0</x><y>-3.5</y></point> </rightBound> <predecessors/> <successors/> </lanelet> <obstacle id="1"> <type>car</type> <role>egoVehicle</role> <initialState> <position><x>5.0</x><y>-1.75</y></position> <velocity><x>8.0</x><y>0.0</y></velocity> <orientation><exact>0.0</exact></orientation> </initialState> </obstacle> </scenario>几个要点值得记下来。坐标单位是米,角度单位是弧度,朝向角orientation的取值范围是 -π 到 π,0 表示指向 x 轴正方向,逆时针为正。速度是 m/s 的向量形式,不是标量。默认时间步长是 0.1 秒,这个值很重要,因为你做的任何时间相关的计算都要基于它。
拿这个时间步长做个换算,你就能感受到它的含义。假设自车速度 30 km/h,换算成米每秒是 30 / 3.6 ≈ 8.33 m/s。在 0.1 秒的时间步里,车前进 8.33 × 0.1 ≈ 0.83 米。如果一个规划器输出的轨迹点是每 0.2 秒一个,而你按 0.1 秒去插值,位置就会整体错位。这类时空一致性问题在对接外部算法时非常常见,务必在加载场景后第一件事就是确认时间步长:
dt = scenario.dt print(f"时间步长: {dt} 秒, 频率: {1/dt} Hz")保存场景 ID 也要注意。场景 ID 一般形如国家代码_地点-编号_类型标识,它不只是一个名字,很多工具会用 ID 去索引场景、管理缓存、拼接结果。如果你手工复制文件后改了名,XML 内部记录的 ID 和文件名对不上,某些接口会直接抛异常。稳妥做法是:改名就一并改 XML 里的 ID,或者走官方接口去设置。
4.2 五行代码加载场景并打印关键信息
加载场景只需要几行:
from commonroad.common.file_reader import CommonRoadFileReader # 传入场景文件路径 scenario, planning_problem_set = CommonRoadFileReader("USA_Lanker-1_1_T-1.xml").open() print("场景 ID:", scenario.scenario_id) print("车道数量:", len(scenario.lanelet_network.lanelets)) print("动态障碍物数量:", len(scenario.dynamic_obstacles)) print("静态障碍物数量:", len(scenario.static_obstacles)) print("时间步长:", scenario.dt)注意.open()返回的是两个对象:场景本身和规划问题集合。场景负责描述"环境长什么样",规划问题负责描述"自车从哪来、要到哪去"。这个分离是有意设计的——同一张地图可以配不同的规划问题,一套地图数据能复用好几次。
规划问题里最关键的是初始状态和目标区域:
for pp_id, pp in planning_problem_set.planning_problem_dict.items(): print("规划问题 ID:", pp_id) print("初始位置:", pp.initial_state.position) print("初始速度:", pp.initial_state.velocity) print("目标区域数量:", len(pp.goal_region))goal_region通常是一个多边形或者一组位置点,表示车辆到达这些区域里任意一个位置就算完成任务。这个设计比"必须到达某个精确坐标点"宽松得多,也更贴近真实驾驶场景,你在写评测逻辑时要理解这个语义,否则会出现"车明明到了,但算没到"的误判。
如果你想查询某个坐标落在哪条车道上,工具提供了现成接口:
lanelet_ids = scenario.lanelet_network.find_lanelet_by_position([[5.0, -1.75]]) print("该位置所在车道:", lanelet_ids)这个接口在调试"车怎么跑到路外面去了"这类问题时非常好用,可以直接把自车每一帧的位置喂进去,看它从哪一帧开始找不到所属车道。
4.3 可视化渲染与导出
加载成功但看不到图,是不踏实的。渲染代码大致如下:
import matplotlib matplotlib.use("Agg") # 服务器上务必加这一行 import matplotlib.pyplot as plt from commonroad.visualization.mp_renderer import MPRenderer # 创建渲染器,figsize 越大越清晰,大场景建议 20x10 以上 renderer = MPRenderer(figsize=(20, 10)) # 依次绘制场景和规划问题 scenario.draw(renderer) planning_problem_set.draw(renderer) # 渲染到画布 renderer.render() plt.savefig("scenario.png", dpi=150, bbox_inches="tight") plt.close()这里有两个实操细节值得说。第一,画布尺寸和分辨率的取舍。一个城市级别的场景动辄几百米宽,你用默认的 6x4 画布画出来就是一团糊。我的经验是把 figsize 设成 20x10,dpi 给 150,出来的图既能看清车道线又能看清车辆,文件大小也还能接受。如果你要批量出几百张图做论文插图,dpi 降到 100 更划算。第二,渲染顺序。先画场景再画规划问题,最后 render,顺序错了会出现图形互相遮挡。如果你要突出显示某几辆车,可以在 render 之后用 matplotlib 的原生接口再叠加图形。
想要矢量图,走 SVG 渲染器:
from commonroad.visualization.svg_renderer import SVGRenderer renderer = SVGRenderer(filename="scenario.svg") scenario.draw(renderer) renderer.render()SVG 的好处是放大不失真,塞进论文或者做汇报都很合适;缺点是复杂场景下文件会变得很大,几百条车道一起画,浏览器打开都要卡一下。
可视化这块还有个小技巧:只画你关心的区域。大场景全量渲染又慢又乱,可以传入一个坐标范围做裁剪,只画自车周围一百米内的内容,查看局部细节时效率会高很多。
5. 踩坑排查实录
5.1 高频报错速查表
安装和使用阶段能遇到的报错五花八门,我把最常见的整理成表,方便你对号入座。
| 报错关键信息 | 常见原因 | 处理方式 |
|---|---|---|
ModuleNotFoundError: No module named 'commonroad' | pip 和 python 不在同一环境 | 改用python -m pip install |
Could not find a version that satisfies the requirement | Python 版本过高或过低,或缺 wheel | 换 3.9/3.10 重建环境 |
error: Microsoft Visual C++ 14.0 or greater is required | Windows 缺编译工具链 | 装 VS Build Tools 或改用预编译包 |
ImportError: libGL.so.1: cannot open shared object file | 容器/服务器缺图形库 | 安装系统级图形依赖,或用 Agg 后端 |
XMLSyntaxError | 场景文件损坏或下载不完整 | 重新下载并校验文件大小 |
ValueError: Scenario ID does not match | 文件名与内部 ID 不一致 | 统一命名,或走官方接口设置 |
TypeError出现在 shapely 调用处 | shapely 2.x 与老代码不兼容 | 锁定 shapely 大版本 |
| 渲染卡死无输出 | 后端问题或场景过大 | 设 Agg 后端,裁剪绘图范围 |
表格里有一个容易被忽略的:场景文件下载不完整。场景集通常打成压缩包分发,网络不稳的时候,下载下来是个残缺的压缩包,解压不报错,但是读某个文件时 XML 解析失败。遇到这种报错先别怀疑代码,去比一下文件大小对不对,通常一分钟就能排除。
5.2 依赖冲突与版本锁定
依赖冲突是 Python 生态的老毛病,CommonRoad 也躲不过。典型场景是这样:你的项目里还装了另一个用numpy的高层库,它要求 numpy 低于某个版本,而commonroad-io的某个版本要求高于某个版本,pip 报一堆ResolutionImpossible。
处理方法我推荐先隔离,再锁定。隔离就是给 CommonRoad 单独建一个环境,不和别的项目混;锁定就是把几个关键依赖的版本写进requirements.txt,团队里所有人用同一份:
numpy==1.24.4 scipy==1.10.1 matplotlib==3.7.2 shapely==2.0.1 commonroad-io==2023.1版本号要根据你实际跑通的组合来写,不要照抄我这个示例,因为版本迭代很快,写死过期的版本反而会引入新问题。正确做法是:在干净环境里装一遍,跑通验证脚本,然后pip freeze把当前环境导出,这份导出结果就是你团队的可复现基线。以后别人装出问题,拿这份文件一比就知道差在哪。
还有个小坑值得提醒:pip install时如果看到 pip 提示"建议升级 pip",别急着跳过。新版 pip 的依赖解析器比旧版聪明不少,升级之后很多ResolutionImpossible会自己消失。
5.3 Windows 和 Linux 上的差异处理
如果你在 Windows 上做开发,在 Linux 服务器上跑批量任务,下面这几点差异会省你不少时间。
编译类依赖。Linux 上装 C++ 依赖通常一条apt命令解决,Windows 上得装 Visual Studio Build Tools,安装包两个 G 起步,装完还要确认环境变量。所以我的习惯是:Windows 上只做代码编写和场景查看,真跑批量计算就扔到 Linux 或者 Docker 里。这样能避开 Windows 上一大半的编译问题。
路径与编码。Windows 的路径反斜杠在 Python 字符串里是转义符,写路径时用原始字符串或者正斜杠:
path = r"C:\data\scenarios\DEU_Gar-1_1_T-1.xml" # 或者 path = "C:/data/scenarios/DEU_Gar-1_1_T-1.xml"第二个写法在 Windows 和 Linux 上都能跑,我更推荐,跨平台迁移时不用改代码。
换行符。前面提过 git 配置,这里再强调一次。如果你在 Windows 上生成场景文件,然后提交到仓库给 Linux 服务器用,不做换行符统一的话,XML 里会混入多余的字符,某些严格的解析器会报错。
文件系统大小写。Linux 区分大小写,Windows 不区分。在 Windows 上Scenario.xml和scenario.xml被当成同一个文件,到了 Linux 上就是两个。团队协作时统一用全小写加下划线命名,能避开这个问题。
6. 上手之后的路线图
6.1 从读场景到写场景
读完别人的场景只是第一步,真正开始产出是从"写场景"开始的。有两种路径。
一种是从现有场景改。加载一个结构相近的场景,把障碍物的初始状态改掉,把目标区域挪一挪,重新保存成新场景。这条路成本最低,适合快速造测试用例。代码大致是加载、修改属性、然后走写入接口保存。
另一种是从地图数据生成。如果你手上有 OpenDRIVE 或者 SUMO 的路网文件,可以用场景设计工具把它转成 CommonRoad 格式,再往里注入交通参与者。这条路适合有地理信息或者交通仿真背景的人,能批量产出场景,但对格式的理解深度要求更高。
我的建议是先走第一条路。理由很实际:手改一个场景,你能立刻看到每一处修改对可视化结果的影响,这个反馈循环最快。改上十几个场景,你对 lanelet 结构、障碍物状态、规划问题的理解会比读十篇文档都扎实。等你能闭着眼睛说出"我改这个字段会影响什么",再去啃格式转换,接受度高得多。
6.2 对接自己的规划器和仿真器
场景有了,下一步是把自己的算法接进来。这里的核心问题是状态表示的对齐。
CommonRoad 里自车的初始状态包含位置、速度、朝向,可能还有横摆角速度、侧偏角等。你的规划器如果用的是另一套状态定义,比如带曲率、带加速度,就需要一个转换层。这个转换层别嫌麻烦,一定要写成独立的模块,别把转换逻辑散落在各处,不然后面调参时会疯。
对接仿真器也是同样的逻辑。仿真器输出的车辆状态每帧喂回 CommonRoad 的环境里做碰撞检查,检查结果再反馈给仿真器决定是否终止。这个循环里最常见的坑是时间步对不上,仿真器跑的是变步长,CommonRoad 是固定 0.1 秒,你要么把仿真器也固定步长,要么在接口层做插值。我踩过这个坑,插值方案在低速场景没问题,高速急转弯时插值出来的位置会失真,最后的结果就是"明明没有碰撞被判定为碰撞"。稳妥做法是尽量固定步长,实在不行,插值之后一定要做一次残差检查。
还有一点:评测指标要照着官方实现来。自己定义一套舒适度、通行效率的指标很容易,但和别人的结果没法比。花点时间把官方的度量工具用起来,让结果站得住脚,这件事的收益远大于多写几个自己的指标。
最后说个我个人在实际使用中的体会。CommonRoad 这套东西,最大的门槛从来不是安装,安装照着流程走,半小时能搞定。真正花时间的是理解它的场景语义——为什么目标区域是个多边形而不是一个点,为什么障碍物的速度是向量而不是标量,为什么规划问题要和场景分离表示。这几个问题想通了,后面无论是写评测脚本还是对接自己的算法,都会顺畅很多。而想通它们最有效的方式,不是看文档,是打开一个真实场景文件,一行一行读下来,对照着可视化结果去理解每个字段在画面上是什么。我当年就是这么干的,一个下午顶得上看了三天资料。