引言
Apollo赛事围绕自动驾驶车辆在城市道路的行驶场景,开展自动驾驶软件算法研究。按照赛题要求,基于本地dreamview进行赛题场景开发测试,测试完成后按照规则提交代码到ApolloStudio线上评测系统进行评测。
赛题1--靠边启动
1.赛题要求:
绕开障碍物驶入主路,且与障碍物保持1米距离。
2.赛题思路:
直接运行,小车并不会驶入主路。一直处于ParkAndGoStageAdjust阶段。

观察日志发现不驶入主路的原因是主车的停车点无效。

定位至GetParkingOutBoundary函数,发现其是用停车位的box去计算的路径边界。

所以,我们用主车的box,即主车的位置,来去确定边界,并删除上方关于停车点的条件限制。
参考open_space_roi_decider算法中的GetParkingBoundary函数,修改代码,使其逻辑与GetParkingBoundary相同:

GetParkingOutBoundary代码主要逻辑为:
1.获取主车的信息,如初始位置,朝向,长宽,box等信息。
2.初始化坐标系,转换至以主车左上为原点的局部坐标系。
3.而后通过GetRoadBoundary函数,计算出道路边界,再根据车位与道路的位置关系将车位边界与道路边界组合。
赛题2--红绿灯路口行人避让
1.赛题要求:
1.等待行人完全走完人行道,主车才可通行。
2.检测到前方的红灯时, 主车停车在停止线前1.5-2.0米处。
2.赛题思路:
首先,直接运行,车辆检测到红绿灯停止,但是行人在人行道时主车就会运行。不符合题目要求。

查找相关traffic rules中模块crosswalk中的CheckStopForObstacle函数。
分析函数代码逻辑分为以下主要步骤:
1.获取障碍物类型,sl坐标、边界,主车的sl边界,为后续if判断提供参考。
2.判断主车与行人障碍物的横向距离,当横向距离大于参数stop_strict_l_distance当路径与主车重合时才会停止,当横向距离小于参数stop_strict_l_distance时,路径重合、走向主车、在主车前方都会停止。
3.当横向距离在两参数之间时,当路径重合时,才停止。这样就造成了当横向距离在两参数之间时,行人路径与主车路径不重合。主车行走的情况。
解决方法:
将限制条件去掉,不管是否路径重合皆停车,即可解决不等待行人过人行道问题。

最后再调整参数满足停止距离即可。
参数列表如下:

赛题3--路口减速通行
1.赛题要求:
1.主车行驶至路口时,需降低车辆速度至5米/秒通过路口,并在通过路口后恢复正常速度
2.赛题思路:
赛题主要目的就是写在经过限速区域时的减速代码。直接运行会发现,在人行道处并不会减速,原因是没有识别crosswalk并增加限速。
参考Apollo内减速带减速代码:

创建junction-speed-limit task并加入到lane follow流水线之中且配置相关依赖。
获取地图中的人行道,并调用Apollo中的函数AddSpeedLimit即可对其添加限速任务。再对前后限速的距离参数进行调整即可将此赛题完成。

此处代码主要是完成:
1.路径上的人行道放入到容器之中。
2.对每个人行道添加速度限制。
参数部分主要是对此区域forward_buffer和backward_buffer进行拓宽,不然会造成在此区域不能立即减速到指定速度。

赛题4--人行道跟车行驶
1.赛题要求:
1.当遇到前方有人行道行人通行时,不得借道绕行。
2.主车需要与前车保持2-2.5米的停车距离
2.赛题思路:
首先,直接运行,车辆会识别满足lane borrow条件,直接借道行驶。

所以我们需要观察lane borrow的主要过程:

代码逻辑如下:
1.检查变道的前置条件。
2.对是否进行变道判断。
3如果变道则确定路径边界,优化路径以及对路径评估,最终选出最终路径。
所以我们对IsNecessaryToBorrowLane函数进行是否借道检查,并对其中的IsBlockingObstacleFarFromIntersection函数检查,发现此函数对标志进行检查,所以我们加入crosswalk此限制条件,即可完成行人在人行道行走时,不可借道的任务。

此处代码逻辑如下:
1.找出参考线中无法借道的标志。
2.如若在不可借道的范围内,则禁止借道,否则则可以借道。
赛题5--道路施工,换道行驶
1.赛题要求:
1.前方道路施工,导致该段道路无法通行。主车应选取旁边匝道通过该区域。
2.赛题思路:
直接运行代码会发现,主车routing选择了最短的路径,也就是直线到达终点,但是这个路无法通行,导致主车一直被堵塞。

在这里尝试lane borrow和lane change没成功,所以直接去改变了routing,让routing直接去选择右方路线。
找到routing中的路径选择策略:A*算法。尝试改进算法,识别障碍物,增加障碍物代价。但并未成功。又去寻找其中的启发函数,尝试直接选右方的路去,也就是最长的路。只需将启发函数符号变化,就可以选择最长的路。

因为其余场景并无换道任务,所以此方法不会影响其他场景的运行。
赛题6--障碍物堵塞,自动泊车
1.赛题要求:
1.遇到障碍物时,主车可以选择绕过这些空置的泊车位,直接前往目的地。
2.到达泊车位时,完成自动泊车。
2.赛题思路:
首先直接运行,车辆会直接被障碍物堵塞。

查找lane follow流水线,发现可以用lane borrow去完成绕行。通过查询日志,找到lane borrow无法完成的原因:右侧无道。

将此行代码删去。并在识别到借道时,小车附近有停车点时,则可以拓宽车道,让小车nudge完成此任务。
从process中找到确定边界的函数DecidePathBounds函数,并找出其中的关键函数GetBoundaryFromNeighborLane。增加如下代码即可完成nudge,绕过障碍物。

增加代码主要逻辑为:
1.获取主车的位置,并检查主车附近是否有停车站。
2.若检测到停车点,则拓宽道路边界,否则不扩展。
绕行完成后发现并不触发泊车场景。

找到场景转换的函数valet_parking_scenario.cc中的IsTransferable函数。发现只有有外部命令的时候才会触发停车场景。所以增加终点检测停车位的代码,如果有停车位自动转换到泊车场景,否则不转换场景。

增加代码主要逻辑如下:
1.获取终点的位置。
2.设置在ENU坐标下的终点位置,为后续函数提供参考。
3.当无外部命令增加停车位id时,检测终点停车位的id赋值给target_parking_spot_id。
最后修改相应的参数:因绕行障碍物转弯的幅度较大,需调整lane borrow的相关参数,同时需调整转入泊车场景的参数,使其在合适的范围内识别出停车位。
相关参数如下,第一幅图主要是减少横向各阶导数的权重,让转弯可以更陡,让主车更快的回到参考线。第二幅图主要是条件进入泊车场景的距离:



