MATLAB 里的驾驶场景设计器Driving Scenario Designer下文简称 DSD是我这几年做自动驾驶弯道仿真的默认起点。它不神秘本质上就是一个带道路编辑器的场景装配台你画出中心线、拉出车道、摆上几台车、配好速度轨迹按下运行就能看到车沿着你画的线跑起来。真正卡住人的从来不是“按钮在哪”而是“为什么这么点”——为什么中心点要多密、为什么速度剖面必须跟路径点一一对应、为什么仿真步长稍大一点车就切出车道。下面我把自己反复折腾出来的一整套流程摊开讲从环境自查到代码级拆解弯道怎么画、速度怎么算、坑怎么填全部是可复制的东西。这篇内容适合两类人看刚接手驾驶仿真、需要在一小时内跑出第一条弯道的新手已经在用 DSD 但总被“车飞出去”“传感器啥也测不到”“导出 Simulink 后场景不动”折磨的同行。全文以 MATLAB 脚本为主线图形界面只作为校验和兜底手段因为脚本可以版本管理、可以参数化、可以批量跑这三点在真实测试流程里比“点得爽”重要得多。1. 先把工具的能力边界摸清楚1.1 弯道为什么是仿真里最“贵”的场景直线段仿真几乎没有技术含量车速恒定、航向恒定、车道边界平行任何一套逻辑都能跑出漂亮结果。弯道就不一样了它同时把四个维度的复杂度拧在一起几何上曲率随弧长变化车辆模型需要持续输出横向加速度感知上车道线从直线变成圆弧拟合算法要用二次或三次曲线去逼近控制上预瞄距离和前视点必须随曲率调整否则纯跟踪算法会切内道评价上横向误差、航向误差、舒适性指标全都跟曲率强相关。这就是为什么很多人直线跑得挺顺一进弯就原形毕露。DSD 在这方面提供的价值不是“画个弯”而是把道路几何、车辆运动学和传感器模型绑在同一个时间轴上。你画的中心线会被拟合成带曲率连续性的道路车辆沿轨迹行驶时每一帧的位置、朝向、速度都是自洽的雷达和摄像头模型拿到的也是同一套真值。这一点很关键——如果道路用一套坐标、车辆用另一套坐标、传感器再用第三套你后面排查问题会疯掉。另一个容易被忽略的点是弯道对时步的敏感性。直线上车辆朝某个方向匀速走步长 0.1 秒的影响无非是位置跳了一米弯道上同样的步长会让航向角每次跳好几度视觉上就是车在一格一格地“拐硬弯”传感器打出去的光束也会跟着抖动。所以弯道场景做得好不好很大程度取决于你对采样率的把控而不是画得多漂亮。1.2 图形界面和脚本各自该干哪一半我见过不少人一上来就打开 App鼠标拖着中心点一个个点点完发现想改半径只能全部推倒重来。这属于用错了工具的分工。我的习惯是探索性设计用界面可复现场景用脚本。第一次试某个弯道形态、想快速看看车道宽度和转弯半径比例是否合理打开drivingScenarioDesigner拖两下确实快但一旦形态确定立刻把中心点坐标抄进脚本后面所有迭代都在代码里做。界面的另一个正确用法是反向校验。脚本跑出来的场景可以一键喂回 AppdrivingScenarioDesigner(scenario)把变量加载进去你就能在图形界面里看到道路边界、车道线、车辆轨迹、传感器覆盖范围叠在一起的样子。有些问题在命令行里看不出来比如两条车道线在弯心处交叉、车辆轨迹贴着路缘擦过去图形界面上一眼就能发现。这种“脚本生成 界面审查”的双向流程比纯界面或纯脚本都高效。还有一个功能值得单独提App 的 Tools 菜单里有一个针对道路和车辆时步的检查项。它会把你的速度、采样时间、道路曲率放在一起算一遍告诉你当前配置下车辆每步移动的距离是否超出了合理范围。这个检查我在项目里救过好几次场——用高车速配 0.1 秒步长时它会直接标红提示省得你后面花两小时去查“为什么车会瞬移”。2. 动手前的环境自查与最小验证2.1 工具箱、版本与常见依赖DSD 不是一个独立产品它挂在自动驾驶工具箱体系下同时依赖场景建模的基础设施。装环境时至少确认这几块自动驾驶相关工具箱提供drivingScenario、vehicle、trajectory、各类传感器生成器、导航与地图相关工具箱部分道路拟合和曲线工具在这里、以及基础的 MATLAB 本体。如果你的流程要接 Simulink 做闭环那 Simulink 和对应的场景读取模块也得在。版本差异是个真实的坑。同一个函数在不同年份的版本里参数名会变尤其是传感器生成器那一块属性名和返回值顺序调整过好几次。我的经验是先锁定一个版本再写代码把版本号写在脚本头部注释里别今天用新版本跑通、明天换台机器用旧版本报错然后花半天怀疑人生。另外函数签名不确定的时候别硬猜直接在命令行敲help 函数名看当前这个版本到底怎么写的比在网上翻三年前的帖子靠谱得多。道路拟合部分需要说明一下它的原理。DSD 在根据你给的中心点生成道路时用的是回旋线clothoid拟合也就是曲率随弧长线性变化的曲线。这个东西在公路设计里是标准做法——直线段曲率是 0圆曲线曲率是常数 1/R中间用回旋线过渡保证曲率连续。好处是车辆沿路行驶时方向盘转角不会突变坏处是你给的中心点如果本身跳变很大拟合出来的道路可能偏离你预期的形状。理解这一点你就知道为什么中心点不能随手点了。2.2 三行代码验证环境是否真的可用在正式画弯道之前我建议先跑一个最小闭环确认整条链路是通的。三行代码就够scenario drivingScenario(SampleTime, 0.05, StopTime, 5); road(scenario, [0 0 0; 50 0 0], Lanes, lanespec([1 1])); drivingScenarioDesigner(scenario);这三行的意思是建一个采样时间 0.05 秒、总时长 5 秒的场景铺一条从原点出发、沿 x 轴方向长 50 米的直路中心线左右各一条车道然后把它丢进图形界面里看一眼。如果界面正常打开、道路显示为两条平行车道线说明基础环境没问题。这一步的价值在于把所有环境问题前置。我见过太多次兴冲冲写了两百行弯道代码一运行报“函数不存在”才开始排查是哪个工具箱没装。用最小用例先探路成本三十秒收益是后面省掉半小时。如果这三行都跑不通那问题一定在环境层面跟你的弯道逻辑毫无关系方向很明确。顺手再验证一下推演机制while advance(scenario) p pose(scenario.Actors(1)); fprintf(t%.2f x%.2f y%.2f\n, scenario.CurrentTime, p(1), p(2)); end这段用来说明advance是怎么推进时间的——每次调用把场景往前推一个采样步长返回 false 表示到StopTime了。后面所有的仿真循环都是这个骨架。3. 弯道场景的三层建模道路、车辆、传感器3.1 道路层中心点密度决定了一切道路是场景的地基弯道尤其如此。核心接口是road(scenario, roadCenters, Lanes, lanespec([...]))其中roadCenters是一个 N×3 的矩阵每行是一个中心点的 [x, y, z] 坐标z 用来表达高程和横坡。DSD 会按顺序把这些点连成一条带曲率连续性的中心线再按lanespec往两侧铺车道。中心点密度这里有两条经验值。第一弯道上相邻中心点的间距控制在曲率半径的 1/10 到 1/5 之间。半径 60 米的弯点间距 6 到 12 米比较合适。间距太大回旋线拟合出来的形状跟你脑子里想的不一样间距太小拟合会放大数值噪声道路出现细微抖动车辆的航向角跟着抖。第二直线段不需要这么密30 米一个点完全够用因为直线曲率恒为零拟合结果没有任何自由度。lanespec的参数形式是lanespec([左车道数, 右车道数])。lanespec([1 1])是双向两车道lanespec([0 1])是只有右侧一条车道。这里有个容易搞混的地方车道的左右是相对于中心线的前进方向定义的右车道的中心线在中心线右侧偏移半个车道宽默认车道宽 3.6 米所以偏移是 1.8 米。如果你的车要跑在右车道正中间初始位置就不能放在中心线上。高程那一列别随便填。很多人图省事全部写 0平面场景下没问题但如果你想验证车辆在带横坡的弯道上是否会有姿态变化第三列就要按横向坡率填。常规公路弯道超高在 2% 到 6% 之间半径 60 米这种中速弯取 4% 比较常见。具体做法是让外弯侧比内弯侧高高差 车道宽 × 坡率。3.2 车辆层位置、朝向与速度剖面车辆用vehicle(scenario, Position, [x y z])创建能配置的包括尺寸、类别、初始朝向。位置坐标是世界坐标系下的米朝向是相对 x 轴逆时针的角度弧度。初始位姿的坑在于它必须跟轨迹的第一个点一致。trajectory的行为是让车辆从当前位姿出发去追击你给的路径点如果第一个路径点跟初始位置差了五米车辆会在第一步就瞬移到那个点上后续的轨迹全部错位。我踩过这个坑现象是车辆在起点闪一下然后整条轨迹看起来像整体平移了一段。真正决定弯道表现的是速度剖面。trajectory(actor, waypoints, speed)的第三个参数可以是标量全程匀速也可以是与waypoints行数相同的向量每点一个速度。用向量的时候必须严格对齐行数多一个少一个都会报错这个报错信息还挺模糊的容易让人卡住。速度剖面的设计原则是从横向加速度约束倒推。车辆过弯时受到的横向加速度近似为 a v²/R这个值直接决定乘客的舒适感。工程上取 1.5 到 2.5 m/s² 作为舒适上限超过 3 m/s² 就明显不适了。所以给定半径 R弯中允许的最大车速是 v √(a·R)。这个公式是整个速度设计的锚点后面具体算的时候会用到。3.3 传感器层装在哪、看多远传感器模型不是必须的但弯道场景不加传感器价值至少少一半。常用的两个是雷达数据生成器和视觉检测生成器。雷达的参数重点是安装位置、视场角和量程。安装位置用MountingLocation单位是米坐标是相对车身、以车辆中心为原点的车身坐标系x 向前、y 向左、z 向上。量产车的毫米波雷达一般装在前保险杠纵向 3 到 4 米、横向 0、高度 0.4 到 0.8 米。视场角用FieldOfView格式是 [水平角, 俯仰角]角雷达通常水平 90 度、俯仰 10 到 20 度。视觉检测器的关键参数是量程、检测概率和误检率。量程要跟你的场景尺度匹配弯道半径 60 米、总长一百多米的话量程 60 米是个合理起点。检测概率默认 0.9 左右意味着每帧有 10% 概率漏检如果你想验证跟踪算法的鲁棒性可以调到 0.7 制造压力。弯道下传感器有个特有现象内弯侧目标被遮挡外弯侧目标提前暴露。这是几何决定的雷达和摄像头都一样。如果你的场景里放了多辆车做评估时要意识到这个不对称性——同一辆目标车在弯道入口和弯道出口的检测稳定性可能完全不同别把它当成算法的稳定性问题。4. 完整实操五分钟搭出一条 90 度弯道4.1 第一步 用脚本生成中心线先定义基本几何参数。我要的是一条从原点出发、走 30 米直线、然后左转 90 度的弯弯道半径 60 米。这几个数字决定了整条路的位置。R 60; % 弯道半径单位 m lineLen 30; % 入弯前直线长度单位 m nLine 7; % 直线段中心点数量 nArc 10; % 弯道段中心点数量 % 直线段沿 x 方向y 恒为 0 sLine linspace(0, lineLen, nLine); ptsLine [sLine, zeros(nLine, 1), zeros(nLine, 1)]; % 弯道段圆心位于 (lineLen, R)从 θ0 转到 θπ/2 theta linspace(0, pi/2, nArc); ptsArc [lineLen R*sin(theta), R - R*cos(theta), zeros(nArc, 1)]; % 拼接时去掉重复的接缝点 roadCenters [ptsLine; ptsArc(2:end, :)];这段代码的几何关系值得说清楚。圆心选在 (30, 60)起点是 (30, 60-60) (30, 0)正好接上直线段的终点终点是 (3060, 60) (90, 60)。参数方程用 sin 和 (1-cos) 的组合起点处切线方向是 x终点处切线方向是 y也就是标准的左转 90 度。拼接时去掉ptsArc的第一个点因为那一点和直线段最后一点完全重合重复的中心点会让后续的弧长计算出现零长度段某些函数会直接报错。如果换成右转、或者转 45 度只需要改theta的范围比如linspace(0, -pi/2, nArc)就是右转。半径和直线长度改了圆心位置要跟着改这个是唯一的联动关系。4.2 第二步 铺车道与摆车scenario drivingScenario(SampleTime, 0.02, StopTime, 25); road(scenario, roadCenters, Lanes, lanespec([1 1]));采样时间我选了 0.02 秒比默认的 0.05 更细。原因后面第 5 节会展开简单说就是弯道上每步位移要控制住不然航向角跳变明显。lanespec([1 1])是双向两车道视觉上完整一些。接着摆车。为了让车跑在右车道中心而不是压着中心线先用中心点的法向量往外偏 1.8 米dx gradient(roadCenters(:,1)); dy gradient(roadCenters(:,2)); nor [-dy, dx] ./ vecnorm([-dy dx], 2, 2); % 单位法向量指向左侧 laneCenters roadCenters(:,1:2) - 1.8 * nor; % 右车道中心 laneCenters(:,3) 0;gradient算的是每个点处切线的差分近似[-dy, dx]旋转 90 度得到法向。注意方向这个法向指向左侧所以减去 1.8 才是右侧车道中心。这个偏移用在整个路径上而不是只偏起点——弯道里法向是连续变化的只偏起点会让车辆越跑越往外。然后创建车辆并把初始位置设到路径第一个点ego vehicle(scenario, ... ClassID, 1, ... Position, laneCenters(1,:), ... Length, 4.6, Width, 1.9, Height, 1.5);4.3 第三步 配轨迹与速度速度剖面按前面说的横向加速度倒推。我取舒适上限 1.5 m/s²半径 60 米aLatMax 1.5; vCorner sqrt(aLatMax * R); % 约 9.49 m/s折合 34 km/h vEnter 14; % 入弯前车速m/s sDecelFrom 10; % 从弧长 10 m 处开始减速vCorner sqrt(1.5 × 60) 9.49米每秒大概 34 公里每小时。入弯前 14 米每秒50 公里每小时所以需要在直线段内减速。按弧长重采样路径点再逐点填速度s [0; cumsum(vecnorm(diff(roadCenters), 2, 2))]; sUniform linspace(0, s(end), 240); waypoints [interp1(s, laneCenters(:,1), sUniform), ... interp1(s, laneCenters(:,2), sUniform), ... zeros(numel(sUniform),1)]; speedProfile vCorner * ones(size(sUniform)); idx1 sUniform sDecelFrom; speedProfile(idx1) vEnter; idx2 sUniform sDecelFrom sUniform lineLen; speedProfile(idx2) vEnter (vCorner - vEnter) * ... (sUniform(idx2) - sDecelFrom) / (lineLen - sDecelFrom); trajectory(ego, waypoints, speedProfile);这里有两个关键动作。一是按弧长等间距重采样把原来疏密不均的中心点变成间隔均匀的点总长约 124 米240 个点间距约 0.52 米这样速度剖面在空间上的分布才是均匀的。二是速度向量长度必须等于路径点行数代码里都用sUniform驱动天然对齐。入弯的减速段是线性的实际车上会做成加加速度受限的 S 形曲线但用于仿真验证横向控制线性减速已经够用而且更容易分析。4.4 第四步 运行与可视化bep birdsEyePlot(XLimits, [-20 130], YLimits, [-40 100]); lp laneBoundaryPlotter(bep, DisplayName, 车道边界); tp trackPlotter(bep, DisplayName, 自车, MarkerFaceColor, red); plotLaneBoundary(lp, scenario.LaneBoundaries); while advance(scenario) p pose(ego); tp(p(1), p(2), p(3)); drawnow limitrate end跑起来之后你应该能看到车从原点出发沿直线跑 30 米在弯道入口减速然后贴着弯道内侧平稳转过 90 度。如果车辆在中途跳出车道或者航向角剧烈摆动直接跳到第 6 节找对应症状。5. 代码逐段拆解与参数怎么算5.1 圆弧参数化与切线方向圆弧参数方程用的是(圆心 R·sinθ, 圆心_y - R·cosθ)这种组合而不是常见的(R·cosθ, R·sinθ)。这个选择不是随意的是因为后者在 θ0 处的切线方向是 y起点朝向不对你还得额外加一个旋转矩阵。用 sin 和 (1-cos) 的形式天然满足“起点在原点方向、切线沿 x”的需求省去一堆坐标变换。对于任意起始朝向的弯道更通用的做法是先算一个旋转矩阵再作用到点上heading0 pi/6; % 起始朝向30 度 Rmat [cos(heading0) -sin(heading0); sin(heading0) cos(heading0)]; ptsArcRotated (Rmat * ptsArc(:,1:2));不过实际项目里我一般不用这套通用解因为场景都是围绕自车坐标系搭建的让自车从原点沿 x 出发后面所有几何都能用最简单的表达式写。保持这个约定能省掉大量调试坐标系的时间。还有一个细节trajectory计算车辆朝向是靠前后路径点的差分所以路径点的顺序必须是从起点到终点单调推进。如果你不小心把点排反了车辆会试图倒着走或者干脆原地打转。中心点拼接顺序错了是新手常见的失误症状是车辆在起点来回抽搐。5.2 弧长重采样为什么必须做不重采样会发生什么你给的中心点可能是 7 个直线点加 10 个弯道点间距分别是 5 米和 10.5 米而速度向量如果按点数配每个点之间代表的空间距离完全不同。线性插值速度的话减速的“陡峭程度”就会随点间距变化弯道入口那一段的减速度可能比你设计的大两三倍。重采样的本质是把时间维度的设计转换到空间维度。速度剖面本质上是 s弧长的函数只有等间距采样速度曲线在空间上的斜率才有物理意义。cumsum(vecnorm(diff(...)))这段就是算累积弧长然后interp1在弧长域上做线性插值把原来不均匀的点映射到均匀网格上。采样密度怎么定我给的经验值是空间分辨率 0.5 米左右对应总长 124 米就是 240 个点。这个密度下60 米半径的弯道每两点之间的转角约 0.5 度视觉上足够平滑。密度再高收益不大反而增加计算量低于 0.3 米的间距就要注意数值噪声了。5.3 横向加速度反推限速这张表是我常用的速查取横向加速度上限 1.5 m/s²弯道半径 R (m)弯中限速 v (m/s)换算 (km/h)典型场景205.519.7停车场掉头306.724.1城市小半径路口508.731.2城市主干道转弯609.534.2郊区匝道8011.039.4快速路匝道10012.244.1高速互通如果你的场景是验证紧急工况可以把上限提到 2.5 m/s²车速相应提高约 29%因为 √(2.5/1.5) ≈ 1.29。但要注意此时车辆模型的轮胎侧偏特性可能进入非线性区仿真结果的参考价值会下降。反过来用也很有价值给定你希望跑的车速反推需要多大的半径。比如验证某个弯道 60 km/h 的通过性v 16.7 m/s在 1.5 m/s² 约束下需要 R v²/a 278 米。这个数字一算出来你就知道60 km/h 过 60 米半径的弯横向加速度会到 4.6 m/s²属于相当激进的工况。5.4 仿真主循环的时序控制采样时间选 0.02 秒不是拍脑袋。判断标准是单步位移量与弯道曲率的关系每步车辆前进的距离 Δs v·Δt如果 Δs 大到让离散化后的轨迹明显偏离圆弧航向角就会出现阶跃。拿弯中 9.5 m/s 算0.02 秒步长对应 Δs 0.19 米圆弧上对应的圆心角是 0.19/60 ≈ 0.18 度完全可以忽略。如果用 0.05 秒Δs 0.475 米转角 0.45 度还凑合用 0.1 秒Δs 0.95 米转角接近 1 度视觉上就能看出车辆“一格一格拐”。高速工况下这个差异更明显——30 m/s 配 0.1 秒步长单步走了 3 米弯道上基本没法看。代价是计算量。0.02 秒步长、25 秒场景就是 1250 步比 0.05 秒多了 2.5 倍。如果场景里有多个传感器、每帧都要做检测跑一次可能要几十秒。我的折中方案是调试阶段用 0.02批量回归用 0.05前者保证可视化正确后者保证吞吐量。真要卡精度可以在弯道段动态调整步长但这就复杂了一般场景用不上。另外StopTime要留余量。按 124 米总长、平均速度 10 m/s 估大约 12.4 秒能跑完我给 25 秒是有富裕的。如果StopTime设小了车辆跑到一半仿真就结束你可能误以为是轨迹断了。6. 踩坑实录弯道仿真最常见的六类问题6.1 车切出车道或“飞出去”这是最高频的问题原因通常有三个按出现频率排序。第一路径点顺序错乱或存在重复点。拼接中心线时如果接缝点没去重cumsum里会出现零长度段interp1在这一点上会产生 NaN轨迹就断了。表现是车辆在某个位置突然消失或跳到远处。检查方法很简单跑之前打一行any(diff(s) 0)返回 true 就说明有非递增的弧长。第二速度剖面长度与路径点数不匹配。这个报错通常直接报“向量长度不一致”但如果你用了广播机制不小心让它跑通了速度就会错位到别的点上车辆会在弯心附近莫名其妙加速。第三车辆初始位姿与路径首点偏离。前面提过偏离超过一两米车辆就会瞬移。检查方法是打印ego.Position和waypoints(1,:)对比差的应该是零。还有一种“飞出去”是真的物理原因——速度太快轨迹函数算出来的转向角跟不上。这时候车辆不是瞬移而是沿着切线方向冲出去路径变成一条直线。看到这个现象先降速再谈别的。6.2 传感器检测不到目标传感器返回空结果排查顺序建议这样走。先确认调用时机传感器生成器需要在advance之后、用当前时刻的scenario.Actors调用如果你在循环外面调那永远拿到的是初始状态。再确认坐标系MountingLocation用的是车身坐标系不是世界坐标系写错了传感器就装到车顶上或者车尾去了。然后是视场角。弯道场景里如果你的传感器水平视场只有 30 度那内弯侧的目标在很长一段距离里都进不了视野这是几何决定的不是 bug。想验证这一点把视场角调到 120 度再跑一遍如果检测突然出现了说明就是视野问题。最后是版本的返回值顺序。不同年份的版本里视觉检测器的返回值可能是[detections, numDetections, image]也可能是别的排列用之前help一下确认别照着旧帖子抄。6.3 导出 Simulink 之后的断层场景在脚本里跑得好好的导出成模型之后车不动或者场景不变问题通常出在这几处。场景数据没跟着模型走。导出时场景是保存成一个数据文件或工作区变量的Simulink 里的场景读取模块需要指向这个变量。如果你换了机器或者清了工作区模型找不到数据就会用默认空场景表现就是路没画出来、车也不动。采样时间不匹配。模型里的求解步长跟场景的SampleTime如果不是整数倍关系车辆位置会出现插值误差弯道上表现为轨迹抖动。建议把模型的固定步长设成与场景采样时间一致或者成整数倍。道路参数没有同步。有些导出方式只把车辆轨迹带过去道路几何要单独配置。这时候模型里车辆是沿着一条不存在的路在跑视觉上就是悬浮。检查方法是在模型里把道路预览打开看边界线是否显示。现象最可能原因快速验证车辆起点闪烁后整体偏移初始位姿与首路径点不一致打印两者差值弯道中车辆沿切线冲出速度超过曲率允许上限按 √(aR) 重算限速轨迹在某个位置断裂路径点重复导致弧长非递增any(diff(s)0)传感器全程无检测调用时机在循环外或视场过窄加大视场角重跑导出后场景空白场景数据未随模型加载检查模块指向的变量弯道上航向角一跳一跳采样步长过大步长减半观察7. 从单车弯道走向多车交互与批量测试7.1 编排一辆对向车单车跑通之后加一辆对向车是验证感知和决策逻辑的下一步。做法是复制路径生成逻辑把车道偏移反过来oppLane roadCenters(:,1:2) 1.8 * nor; % 左侧车道 oppLane(:,3) 0; % 对向车从弯道出口往入口跑路径需要反转 oppWaypoints flipud(oppLane); oppSpeed 8 * ones(size(oppWaypoints,1), 1); opp vehicle(scenario, ClassID, 1, Position, oppWaypoints(1,:)); trajectory(opp, oppWaypoints, oppSpeed);关键点是路径必须反转。对向车的行驶方向跟自车相反如果不翻转它会从弯道出口倒着开回入口虽然位置对但朝向完全错看起来像在倒车。翻转之后还要检查速度向量的长度也跟着变这一点容易漏。时间上的编排更有讲究。你想让两车在弯心交会还是错开交会时机取决于两车进入场景的时间差。trajectory支持延迟启动可以在路径前面补一段静止点或者干脆调整初始位置。我一般用初始位置控制直观一些把对向车的起点往远处挪让它晚一点进入交互区域。7.2 把场景参数化以便批量跑单次仿真说明不了什么真正的价值在于跑一批参数组合。把整个搭场景过程包成函数是最省事的做法function scenario buildCurveScenario(R, vEnter, aLatMax, dt) if nargin 4, dt 0.02; end vCorner sqrt(aLatMax * R); ... % 内部按前面的逻辑生成道路、车辆、轨迹 end这样外层就能用嵌套循环扫参数results []; for R [40 60 80 100] for v0 [10 12 14 16] sc buildCurveScenario(R, v0, 1.5, 0.05); metric runAndEvaluate(sc); % 自己写的评估函数 results [results; R, v0, metric]; end end评估函数里我通常记三个指标最大横向误差车辆实际位置到车道中心线的距离峰值、最大横向加速度从速度平方除以曲率算、航向误差收敛时间从入弯到误差稳定在设计值以内的时间。这三个放在一起基本能判断某个控制参数在弯道上的适应性。批量跑有个坑drivingScenario对象是有状态的跑完之后不能直接复用必须重新创建。循环里每次都调buildCurveScenario就是因为这个别图省事在循环外建一次然后反复advance。另外每次创建场景的随机种子要固定传感器模型里有随机成分种子不固定的话同一组参数跑两次结果不一样你会误以为算法不稳定。我个人在实际操作中的体会是弯道仿真这件事八成的时间花在“把几何和速度对上”两成花在“看结果”。第一版场景能跑起来之后别急着加传感器、加多车先把单车轨迹跑顺、把横向误差曲线画出来确认车辆确实贴着车道中心走。这个基线稳了后面加什么都是增量验证基线不稳后面每加一个元素你都要重新怀疑一遍到底是谁的问题。
阅读完成 · 觉得有帮助?