1. 规划模块在Apollo架构中的真实位置
很多人第一次接触Apollo的规划模块,容易把它当成一个孤立的“路径生成器”——输入起点终点,输出一条轨迹,完事。但实际跑过Apollo代码或者调过实车的人都知道,规划模块是整个自动驾驶系统里最“夹心层”的存在:上游要消化感知、定位、地图、预测四个模块的输出,下游要给控制模块喂一条带速度信息的、时间对齐的、可执行的轨迹。任何一层的数据抖动,最后都会在规划这里集中爆发。
我在一个模拟项目里做过统计,规划模块收到的输入数据里,大约有30%的帧存在不同程度的“脏数据”——定位跳变、预测障碍物轨迹置信度骤降、地图匹配失败等等。规划模块不能拒绝这些输入,它必须在有限的时间内(通常100ms以内)给出一个“当前最优解”。这就决定了Apollo规划模块的设计哲学:不是追求全局最优,而是追求在约束条件下的实时可行。
Apollo的规划模块在架构上分成三个核心层次,这个分层逻辑值得先讲清楚,因为后面所有的细节都挂在这三个层次上:
- 决策层(Decision):回答“要不要变道、要不要跟车、要不要让行”这类离散问题。它输出的是一组决策标签,比如
CHANGE_LANE_LEFT、FOLLOW、STOP。 - 优化层(Optimization):在决策层给定的约束下,求解一条平滑的、满足车辆运动学约束的轨迹。Apollo主要用基于二次规划的离散点优化,以及基于样条曲线的平滑。
- 规划层(Planning):把优化后的轨迹做时间维度的速度规划,最终输出带时间戳的轨迹点序列。
这三层不是串行流水线,而是有反馈回路的。决策层如果发现优化层求解失败,会降级到更保守的决策;优化层如果发现速度规划不可行,会反过来调整路径的曲率约束。这种“层间协商”机制是Apollo规划模块比较独特的地方,也是很多自研规划系统容易忽略的。
提示:如果你在调试规划模块时发现轨迹频繁跳变,先别急着改优化器的权重参数,大概率是决策层的状态机在相邻帧之间发生了震荡。优先检查决策状态的切换条件是否加了滞回区间。
2. 从感知到规划:数据流的清洗与对齐
规划模块拿到的原始数据,质量参差不齐。感知模块输出的障碍物列表,可能包含大量误检的“幽灵障碍物”;预测模块输出的轨迹,可能因为模型推理延迟而比实际时间戳晚了50ms;定位模块在隧道或高架下可能突然丢失RTK信号,导致位置跳变几米。这些数据如果不做清洗和对齐,直接喂给规划器,出来的轨迹要么急刹要么画龙。
2.1 障碍物列表的过滤策略
Apollo在规划模块的入口处有一个ObstacleFilter,它的过滤逻辑不是简单的“置信度低于阈值就丢弃”。我实际读过这部分代码,它做了几件事:
第一,基于历史轨迹的一致性校验。如果一个障碍物在连续5帧里只出现了2帧,而且位置跳变超过2米,它会被标记为“低可信”,但不会立即删除,而是放入一个“观察池”。只有连续3帧稳定出现后,才会进入规划器的考虑范围。这个设计很巧妙——它避免了因为单帧误检导致规划器做出过度反应(比如突然急刹),同时也保留了真实障碍物突然出现的响应能力。
第二,基于地图先验的过滤。如果障碍物的位置落在已知的静态障碍物区域(比如地图里标注的隔离带、桥墩),而且它的速度接近零,那么它会被直接忽略。这个逻辑依赖高精地图的准确性,如果地图本身有偏差,反而会引入新的问题。
第三,基于运动学可行性的过滤。如果一个障碍物的预测轨迹显示它能在1秒内从0加速到100km/h,或者转弯半径小于车辆最小转弯半径,那么这个预测轨迹会被判定为“物理不可行”,规划器会退回到使用障碍物的当前位置和速度做保守估计。
# 伪代码示意:障碍物过滤的核心逻辑 def filter_obstacles(obstacles, history_buffer, map_info): filtered = [] for obs in obstacles: # 历史一致性检查 if not is_consistent(obs, history_buffer): observation_pool.add(obs) continue # 地图先验过滤 if is_in_static_region(obs, map_info) and obs.speed < 0.1: continue # 运动学可行性检查 if not is_kinematically_feasible(obs.predicted_trajectory): obs.predicted_trajectory = conservative_estimate(obs) filtered.append(obs) return filtered2.2 时间戳对齐的坑
时间戳对齐是规划模块里最容易被低估的环节。Apollo的规划周期是100ms,但感知和预测模块的输出频率可能不是严格的10Hz。我遇到过的情况是:预测模块因为模型推理耗时波动,输出的轨迹时间戳有时候是当前帧的,有时候是上一帧的。如果规划器直接用这些轨迹做碰撞检测,就会出现“明明障碍物已经过去了,规划器还在刹车”的诡异现象。
Apollo的解决方案是在规划模块内部维护一个时间对齐缓冲区。所有输入数据先进入缓冲区,规划器每次从缓冲区里取“最接近当前规划时刻”的数据。如果某个模块的数据延迟超过200ms,规划器会触发降级策略——比如把障碍物的预测轨迹替换为匀速直线运动模型,或者直接忽略该障碍物的预测信息,只用当前位置做保守避让。
这个降级策略的触发阈值是可以配置的。我在模拟项目里把阈值从200ms调到150ms后,规划器的响应更灵敏了,但误刹车的频率也上升了。后来折中到180ms,配合一个“连续3帧延迟才降级”的条件,效果比较平衡。
注意:时间对齐缓冲区的深度不要设得太大。缓冲区越深,规划器用的数据越“旧”,在高速场景下这可能是致命的。一般建议缓冲区深度不超过3帧。
3. 决策层的状态机设计与震荡抑制
决策层是规划模块里最“像人”的部分。它要回答的问题不是数学优化能直接解决的,而是需要一套规则和状态机来处理的离散决策。Apollo的决策层用了一个分层状态机,顶层是场景状态(比如“跟车”、“变道”、“路口通行”),底层是具体的行为状态(比如“加速跟车”、“减速跟车”、“等待变道”)。
3.1 状态机的层级结构
顶层场景状态之间的切换条件比较严格。比如从“跟车”切换到“变道”,需要同时满足:相邻车道有足够的安全间隙、本车道前车速度低于期望速度的80%、变道意愿持续超过2秒。这三个条件缺一不可。我见过一些自研系统只用了“相邻车道有间隙”一个条件,结果就是车辆在拥堵路段频繁尝试变道,实际根本变不过去,反而影响了本车道的通行效率。
底层行为状态的切换就灵活得多。以“跟车”场景为例,底层有“加速跟车”、“匀速跟车”、“减速跟车”三个状态。切换条件主要看与前车的时距(Time Gap)和相对速度。时距大于2.5秒切加速,1.5到2.5秒之间切匀速,小于1.5秒切减速。这些阈值不是拍脑袋定的,而是根据大量实车数据标定的——时距小于1.5秒时,人类驾驶员的刹车反应时间中位数大约是0.8秒,加上系统延迟,必须提前减速才能保证安全。
3.2 震荡抑制的三种手段
状态机震荡是规划模块调试中最常见的问题。表现就是车辆在两种行为之间反复横跳,比如一会儿加速一会儿减速,乘坐体验极差。Apollo用了三种手段来抑制震荡:
第一种是滞回区间。比如从“匀速跟车”切到“减速跟车”的时距阈值是1.5秒,但从“减速跟车”切回“匀速跟车”的阈值是1.8秒。这0.3秒的差值就是滞回区间,它防止了在阈值附近来回切换。
第二种是最小持续时间。任何状态一旦进入,至少保持0.5秒。这个约束在代码里是通过一个state_entry_time变量实现的,每次状态切换时检查当前时间减去进入时间是否大于最小持续时间。
第三种是决策平滑。即使状态发生了切换,输出的决策指令也不是阶跃变化的。比如从“加速跟车”切到“减速跟车”,加速度指令不是从+1.0直接跳到-1.0,而是经过一个0.3秒的线性过渡。这个过渡在优化层实现,但决策层会把这个过渡时间作为约束传给优化层。
// 伪代码示意:带滞回和最小持续时间的状态切换 bool should_transition(State current, State target, double time_gap, double time_in_state) { if (time_in_state < MIN_STATE_DURATION) return false; if (current == CRUISE && target == DECELERATE) { return time_gap < 1.5; // 进入减速的阈值 } if (current == DECELERATE && target == CRUISE) { return time_gap > 1.8; // 退出减速的阈值(滞回) } // ... 其他状态对 }我在实际调试中发现,滞回区间的宽度需要根据场景动态调整。在高速场景下,时距的波动比较大,滞回区间可以设宽一点(0.5秒);在低速拥堵场景下,时距变化快,滞回区间要窄一点(0.2秒),否则车辆对前车起步的反应会太迟钝。
4. 优化层的轨迹生成:从离散点到平滑曲线
优化层是规划模块里数学味最浓的部分。它的任务是在决策层给定的约束下,生成一条满足车辆运动学约束、舒适性约束、安全性约束的轨迹。Apollo主要用了两种优化方法:基于二次规划的离散点优化,和基于样条曲线的平滑。
4.1 二次规划问题的构建
Apollo的离散点优化把轨迹表示为一组离散的路径点,每个点有x、y坐标和航向角。优化变量就是这些点的位置和航向角。目标函数通常包含三项:路径平滑度(相邻点之间的曲率变化最小)、与参考线的偏离度(尽量贴着车道中心线走)、与障碍物的距离(保持安全距离)。
约束条件包括:车辆运动学约束(曲率不超过最大曲率)、边界约束(路径点必须在可行驶区域内)、连续性约束(相邻点之间的航向角变化不超过阈值)。
这个二次规划问题的规模取决于离散点的数量。Apollo默认用20个点,优化变量大约60个(每个点3个变量)。求解器用的是OSQP,在车载计算平台上求解一次大约需要5-10ms。如果点数增加到50个,求解时间会上升到30ms以上,在100ms的规划周期里就有点紧张了。
我试过把点数从20增加到30,路径的平滑度确实有提升,但求解时间的增加导致规划周期偶尔超时。后来折中到25个点,配合一个“如果求解超时就退回上一帧轨迹”的降级策略,整体表现比较稳定。
4.2 样条曲线平滑的适用场景
二次规划出来的离散点轨迹,虽然满足了约束,但曲率可能不连续——相邻点之间的曲率有跳变。这会导致控制模块在执行时出现方向盘抖动。Apollo在二次规划之后,会再用样条曲线做一次平滑。
样条曲线平滑的核心思想是:用分段多项式(通常是五次多项式)拟合离散点,保证曲率连续。五次多项式可以保证位置、速度、加速度、曲率都连续,适合做轨迹平滑。但五次多项式的计算量比二次规划大,而且对离散点的噪声比较敏感。如果二次规划出来的点本身抖动很大,样条平滑反而会放大抖动。
所以Apollo的策略是:二次规划负责“粗调”,样条平滑负责“精调”。二次规划的输出必须满足一个“平滑度阈值”,如果离散点的曲率变化超过阈值,说明二次规划的结果质量不高,这时候会跳过样条平滑,直接输出二次规划的结果,同时触发一个“规划质量降级”的警告。
提示:如果你在调试时发现轨迹的曲率不连续,先检查二次规划的平滑度权重是否设得太低。平滑度权重太低,二次规划会优先贴着参考线走,导致曲率跳变;权重太高,轨迹会偏离参考线太远,影响通行效率。一般建议平滑度权重在0.1到0.5之间。
4.3 速度规划的耦合问题
路径规划完成后,速度规划是另一个独立的优化问题。Apollo的速度规划把路径上的每个点赋予一个速度值,优化目标是:舒适性(加速度和加加速度最小)、效率(尽量接近期望速度)、安全性(满足与前车的时距约束)。
路径规划和速度规划虽然是分开做的,但它们之间存在耦合。比如路径的曲率会影响最大允许速度——曲率越大,允许的速度越低。Apollo的处理方式是:在速度规划之前,先根据路径曲率计算一个“曲率限速”,把这个限速作为速度规划的硬约束。如果路径规划出来的曲率太大,导致曲率限速低于一个阈值(比如10km/h),速度规划会反馈给路径规划,要求重新规划一条曲率更小的路径。
这个反馈机制在实际调试中很容易出问题。我遇到过的情况是:路径规划和速度规划互相“踢皮球”——路径规划说曲率已经最小了,速度规划说限速太低没法满足时距约束。最后发现是参考线本身有问题——参考线在弯道处的曲率计算有误,导致路径规划一直在试图“修正”一个不存在的曲率问题。
5. 规划模块的调试工具与实战技巧
规划模块的调试不像感知模块那样有直观的图像输出,它的输出是一堆数字和轨迹点。如果没有好的调试工具,排查问题就像盲人摸象。Apollo提供了一套基于可视化的调试工具,但实际用起来还是有一些技巧。
5.1 轨迹可视化的正确打开方式
Apollo的规划模块可以输出多种格式的调试信息,包括:规划轨迹(带时间戳的轨迹点序列)、参考线(规划器使用的参考路径)、障碍物列表(规划器实际考虑的障碍物)、决策状态(当前场景状态和行为状态)。
把这些信息叠加显示在同一个视图里,是排查问题的第一步。我习惯用不同颜色区分:参考线用灰色,规划轨迹用绿色,障碍物用红色,决策状态用文字标注在轨迹起点。这样一眼就能看出规划轨迹是否偏离了参考线、是否与障碍物保持了安全距离、决策状态是否合理。
但要注意,可视化工具显示的轨迹是“规划器认为的轨迹”,不是“控制模块实际执行的轨迹”。两者之间可能有偏差,因为控制模块有自己的跟踪误差。如果发现可视化轨迹没问题但车辆实际表现异常,问题可能出在控制模块,而不是规划模块。
5.2 日志分析的三个关键指标
Apollo的规划模块会输出大量日志,但真正需要关注的指标不多。我总结下来,有三个指标最能反映规划模块的健康状况:
第一个是规划周期耗时。正常情况下应该在50ms以内,如果超过80ms,说明优化问题的规模太大或者求解器收敛太慢。这时候需要检查离散点数量、约束数量是否合理。
第二个是优化求解的迭代次数。OSQP求解器如果迭代次数超过100次还没收敛,说明问题可能是不可行的(约束之间互相矛盾)。这时候需要检查约束条件是否有冲突,比如路径边界约束和障碍物避让约束是否矛盾。
第三个是决策状态切换频率。如果某个场景下决策状态每秒切换超过3次,说明状态机的滞回区间设得太窄,或者输入数据的噪声太大。这时候需要先检查输入数据的质量,再调整滞回参数。
# 从规划日志中提取关键指标的示例命令 grep "planning_time" planning.log | awk '{print $2}' | sort -n | tail -10 grep "solver_iterations" planning.log | awk '{sum+=$2; count++} END {print sum/count}' grep "state_transition" planning.log | awk '{print $3}' | uniq -c | sort -rn | head -55.3 实车调试的注意事项
在模拟环境里调好的规划参数,搬到实车上往往需要重新标定。原因有几个:模拟环境的感知数据是“干净”的,实车的感知数据有噪声;模拟环境的车辆模型是理想的,实车的执行器有延迟和死区;模拟环境的交通流是脚本化的,实车的交通流是随机的。
我在实车调试时踩过的一个坑是:模拟环境里把跟车时距设成了1.2秒,实车上发现这个时距在拥堵路段太激进,前车稍微一减速,本车就不得不急刹。后来把时距调到1.8秒,同时把减速状态的触发阈值从1.5秒调到2.0秒,乘坐体验明显改善。
另一个坑是变道决策的时机。模拟环境里变道决策的触发条件是“相邻车道间隙大于1.5倍车长”,实车上发现这个条件太宽松——1.5倍车长的间隙在高速上看起来很大,但考虑到相对速度,实际变道时后车可能已经逼近了。后来把条件改成“间隙大于2倍车长,且后车相对速度小于5km/h”,变道成功率大幅提升。
注意:实车调试规划模块时,一定要先在一个封闭场地里做低速测试,确认基本的跟车、停车、避让功能正常,再上公开道路。公开道路上的交通流不会给你“重来一次”的机会。
6. 规划模块与其他模块的边界与协作
规划模块不是孤岛,它和感知、预测、控制、地图都有明确的边界。这些边界如果定义不清,就会出现“三个和尚没水喝”的局面——每个模块都以为别人会处理某个问题,结果谁都没处理。
6.1 与预测模块的责任划分
预测模块输出的是障碍物的未来轨迹,规划模块基于这些轨迹做避让决策。但预测模块的轨迹是有不确定性的,规划模块不能假设预测轨迹100%准确。Apollo的做法是:预测模块除了输出轨迹,还输出一个“置信度”和“不确定性范围”。规划模块在避让时,会根据置信度调整安全距离——置信度低,安全距离就大一些。
这个机制在实际运行中有一个问题:如果预测模块对所有障碍物都输出低置信度,规划模块就会变得过于保守,频繁刹车。我遇到过的情况是:预测模块的模型在雨天场景下性能下降,对所有障碍物的置信度都降到了0.5以下,结果规划模块把安全距离拉大到了正常值的2倍,车辆在雨天变得“畏手畏脚”。
后来我们在规划模块里加了一个“置信度平滑”逻辑:如果某个障碍物的置信度在连续10帧里都低于阈值,但它的位置和速度一直很稳定,那么规划模块会逐渐提高对这个障碍物的置信度。这个逻辑基于一个假设:稳定的低置信度比波动的低置信度更可信。
6.2 与控制模块的接口约定
规划模块输出的是轨迹,控制模块负责跟踪这条轨迹。两者之间的接口有两个关键约定:时间对齐和轨迹格式。
时间对齐的意思是:规划模块输出的轨迹点带有时间戳,控制模块必须按照这些时间戳来执行。如果控制模块的执行周期和规划模块的规划周期不一致(比如控制是50Hz,规划是10Hz),控制模块需要在两个规划点之间做插值。Apollo的控制模块用的是线性插值,但线性插值会导致加速度不连续。更好的做法是用样条插值,但计算量更大。
轨迹格式的约定包括:轨迹点的坐标系(通常是车辆坐标系)、轨迹点的属性(位置、速度、加速度、曲率)、轨迹的起止时间。这些格式如果不统一,控制模块解析轨迹时就会出错。我见过一个案例:规划模块输出的轨迹点用的是绝对坐标系,控制模块以为是车辆坐标系,结果车辆直接往反方向开。
6.3 与地图模块的依赖关系
规划模块严重依赖高精地图。地图提供了车道线、路口、限速、交通标志等信息,这些是规划决策的基础。但地图不是永远准确的——道路施工、临时改道、地图数据过期,都会导致地图信息与实际不符。
Apollo的处理方式是:规划模块在使用地图信息时,会做一个“一致性检查”。比如地图显示前方是直道,但感知模块检测到的车道线是弯的,这时候规划模块会优先信任感知模块的车道线,同时触发一个“地图不一致”的警告。这个警告会上报到云端,用于地图更新。
但在实际运行中,感知模块的车道线检测也会出错。如果感知和地图不一致,规划模块需要判断哪个更可信。Apollo的策略是:在结构化道路上优先信任地图,在非结构化道路上优先信任感知。这个策略基于一个经验:结构化道路的地图更新频率高,准确性有保障;非结构化道路的地图数据往往比较粗糙,感知更可靠。
7. 规划模块的性能优化与降级策略
规划模块运行在车载计算平台上,算力有限。当系统负载高的时候,规划模块必须能够降级运行,保证核心功能不丢失。Apollo的降级策略是分级的,从“完全正常”到“最小安全”,中间有多个级别。
7.1 计算负载的监控与响应
规划模块内部有一个“负载监控器”,它实时统计每个子模块的耗时。如果总耗时超过规划周期的80%(比如80ms),负载监控器会触发降级。降级的第一步是减少离散点的数量——从25个点降到15个点,求解时间可以减少大约40%。如果耗时还是超标,第二步是关闭样条平滑,直接输出二次规划的结果。第三步是简化障碍物避让约束,只考虑最近的3个障碍物。
这些降级策略的触发阈值是可以配置的。我在模拟项目里把第一步的触发阈值从80%调到70%,规划模块在负载高的时候更早降级,但轨迹质量下降也更明显。后来折中到75%,配合一个“连续3个周期超标才降级”的条件,避免了因为偶发的耗时波动而频繁降级。
7.2 传感器故障时的规划降级
如果定位模块丢失信号,规划模块会切换到“航位推算”模式——用车辆的速度和航向角推算当前位置。这个模式的精度会随时间下降,所以规划模块会同时降低最大允许速度,并增大安全距离。如果定位丢失超过5秒,规划模块会触发“最小风险策略”——靠边停车。
如果感知模块故障,规划模块会失去障碍物信息。这时候规划模块会切换到“盲行”模式——只依赖地图信息做规划,速度限制在20km/h以下,并且频繁鸣笛(如果车辆有鸣笛功能)。这个模式的风险很高,一般只在低速场景下使用。
如果地图模块故障,规划模块会失去参考线。这时候规划模块会切换到“自由空间规划”模式——用感知模块检测到的可行驶区域做规划,不再依赖车道线。这个模式的轨迹质量会下降,但至少能保证车辆不会撞上障碍物。
提示:降级策略的触发条件一定要做“去抖动”处理。比如定位丢失的判定,不能因为单帧丢失就触发降级,而应该连续丢失3帧才触发。否则在隧道等信号不稳定的场景下,规划模块会频繁在正常和降级之间切换。
7.3 规划失败的恢复机制
规划模块偶尔会求解失败——二次规划问题不可行,或者求解器不收敛。这时候规划模块不能直接输出“无轨迹”,因为控制模块需要一条轨迹来执行。Apollo的恢复机制是:退回上一帧的轨迹,同时把车辆的速度限制在当前速度的80%,并触发一个“规划失败”的警告。
退回上一帧轨迹的策略在低速场景下没问题,但在高速场景下可能危险——上一帧的轨迹可能已经不适合当前的路况了。所以Apollo在高速场景下会做一个“轨迹外推”:用上一帧轨迹的最后几个点,结合当前的速度和航向角,外推一条短轨迹(大约1秒的时长)。这条外推轨迹的精度不高,但至少能给控制模块一个“缓冲时间”,让规划模块有机会在下一帧重新求解。
我在实际调试中发现,规划失败的频率和参考线的质量高度相关。如果参考线在弯道处的曲率计算有误,二次规划问题很容易变得不可行。后来我们在参考线生成阶段加了一个“曲率平滑”步骤,规划失败的频率下降了大约70%。
8. 从规划模块延伸出的几个思考
规划模块的调试和优化是一个长期的过程,没有“一劳永逸”的参数配置。不同的道路场景、不同的交通流、不同的车辆特性,都需要不同的规划策略。我在这个过程中积累了一些体会,可能对正在做类似工作的同行有些参考价值。
第一,规划模块的很多问题,根源不在规划模块本身。定位跳变、感知误检、预测不准、地图过期,这些问题都会在规划模块的表现上暴露出来。如果发现规划轨迹异常,先别急着改规划参数,花点时间检查上游模块的数据质量,往往能事半功倍。
第二,规划模块的参数没有“最优值”,只有“最适值”。同一个参数,在高速场景下表现好,在拥堵场景下可能就很差。与其追求一套“万能参数”,不如设计一套“场景自适应”的参数调整逻辑。Apollo的场景状态机其实就承担了这个角色——不同的场景状态对应不同的参数组。
第三,规划模块的降级策略比正常策略更重要。正常策略决定车辆“开得好不好”,降级策略决定车辆“能不能安全停下来”。在资源有限的情况下,优先保证降级策略的可靠性,再优化正常策略的舒适性。
第四,规划模块的调试需要“数据驱动”。靠直觉调参的时代已经过去了。Apollo的规划模块支持记录大量的调试数据,包括每一帧的输入、输出、中间变量。把这些数据收集起来做统计分析,能发现很多靠人工调试发现不了的问题。比如某个参数在特定场景下的分布异常,或者某个决策状态的切换频率在特定路段突然升高。
第五,规划模块的演进方向是“学习+规则”的混合。纯规则的方法在应对复杂交通流时越来越吃力,纯学习的方法又缺乏可解释性和安全保障。Apollo最近的版本里,决策层开始引入一些基于学习的预测模型,但优化层仍然保持基于规则的二次规划。这种混合架构可能是未来一段时间的主流。
最后分享一个我在调试规划模块时常用的小技巧:把规划轨迹和控制轨迹叠加显示。规划轨迹是规划模块“想走的路”,控制轨迹是车辆“实际走的路”。两者之间的偏差反映了控制模块的跟踪误差。如果偏差在弯道处特别大,说明控制模块的前馈补偿不够;如果偏差在直线段也很大,说明规划轨迹的曲率不连续,控制模块在“追”一条抖动的轨迹。这个技巧帮我定位过好几次问题,比单纯看规划轨迹有效得多。