EM Planner算法概述
EM Planner(Expectation-Maximization Planner)是百度Apollo自动驾驶平台中的一种面向L4级别的实时运动规划算法。该算法通过顶层多车道策略选择参考路径,并在Frenet坐标系下进行车道级的路径和速度规划。EM Planner的设计充分考虑了无人车的安全性、舒适性和可扩展性,能够适应高速公路和低速城区等多种场景。该算法通过动态规划(Dynamic Programming)和基于样条的二次规划(Quadratic Programming)实现,展现了较高的可靠性和低耗时性。
EM Planner的工作原理
EM Planner的工作流程包括以下关键步骤:
多车道策略:算法首先通过搜索算法结合代价估算形成变道策略,将多车道策略划分为被动变道和主动变道,后者考虑动态障碍物而做出决策。
路径-速度迭代算法:在Frenet坐标下,EM Planner通过迭代地进行路径和速度最优求解,考虑与障碍物的交互,上一帧的速度规划将帮助下一帧的路径规划。
决策和交通规则约束:交通规则作为硬约束,与障碍物的交互作为软约束。决策模块为规划带来更明确的意图,减少最优求解的搜索空间。
SL和ST投影:在E-step中,障碍物被投影到车道Frenet坐标系,包括静态和动态障碍物。在M-step中,障碍物在速度时间(ST)图中与生成的速度信息进行估计。
DP路径和QP路径:通过Dynamic Programming获得一个粗略的解,并构建一个凸的通道,然后使用基于Quadratic Programming的样条曲线生产光滑路径。
参考线轨迹决策:最后,参考线轨迹决策器根据当前车辆状态、相关约束和每条轨迹的代价,决定最优的轨迹。
1. 多车道策略
在多车道策略中,EM Planner采用了一种分层决策机制,将策略划分为主动变道 (Proactive Lane Changing) 和被动变道(Reactive Lane Changing)。主动变道策略通常由导航需求触发,如到达目的地所需的车道变更。被动变道则是在当前车道受阻时(例如,前方有障碍物或车辆),系统自动选择另一条车道以确保安全行驶。这种策略通过结合搜索算法和代价估算,能够智能地预测和选择最佳变道路径,同时考虑动态障碍物的影响,确保决策的安全性和效率性。
主动变道
主动变道通常是由导航系统触发的,例如当车辆需要在前方路口左转或右转时,系统会提前规划并选择正确的车道。这种变道策略需要考虑目的地、交通规则、车道可用性等因素。
cpp
*
void proactiveLaneChange(const NavigationData &navData, VehicleState &vehicleState) {
*
// 假设navData包含目的地信息和当前路口信息
*
// vehicleState包含车辆当前状态和位置
*
*
// 检查是否需要变道
*
if (navData.needsTurn() && vehicleState.getCurrentLane() != navData.getTargetLane()) {
*
// 执行变道逻辑
*
vehicleState.changeLane(navData.getTargetLane());
*
}
*
}
AI写代码cpp
运行

被动变道
被动变道是在当前车道受阻时自动触发的,例如前方有慢车或障碍物。系统会评估周围车道的情况,并选择一条安全的车道进行变道,以避免碰撞并保持行驶效率。
cpp
*
void reactiveLaneChange(VehicleState &vehicleState, const std::vector<Obstacle> &obstacles) {
*
// 假设vehicleState包含车辆当前状态和位置
*
// obstacles是一个包含所有障碍物的向量
*
*
// 检查当前车道是否有障碍物
*
for (const auto &obstacle : obstacles) {
*
if (obstacle.isBlocking(vehicleState.getCurrentLane()) &&
*
!obstacle.isTooCloseForSafePassing(vehicleState)) {
*
// 寻找安全的车道进行变道
*
int safeLane = findSafeLane(vehicleState, obstacles);
*
if (safeLane != -1) {
*
vehicleState.changeLane(safeLane);
*
break;
*
}
*
}
*
}
*
}
*
*
int findSafeLane(const VehicleState &vehicleState, const std::vector<Obstacle> &obstacles) {
*
// 遍历所有车道,寻找没有障碍物或障碍物距离安全的车道
*
for (int lane = 0; lane < MAX_LANES; ++lane) {
*
if (!isLaneBlocked(obstacles, lane)) {
*
return lane;
*
}
*
}
*
return -1; // 如果所有车道都被阻塞,返回-1
*
}
*
*
bool isLaneBlocked(const std::vector<Obstacle> &obstacles, int lane) {
*
// 检查指定车道是否有障碍物
*
for (const auto &obstacle : obstacles) {
*
if (obstacle.isInLane(lane)) {
*
return true;
*
}
*
}
*
return false;
*
}
AI写代码cpp
运行

2. 路径-速度迭代算法
在Frenet坐标系下,EM Planner采用了路径-速度迭代算法。这一过程涉及两步:首先,算法基于当前车辆状态和环境信息,通过迭代优化求解最优路径。然后,算法利用上一帧的速度规划信息来指导下一帧的路径规划,形成一个闭环反馈机制。这一算法的关键在于,它能够同时考虑路径的平滑性和速度的合理性,以及障碍物的动态变化,通过迭代优化实现路径规划与速度控制的协同优化。
第一步:路径优化
- 初始化:基于当前车辆状态(位置、速度)和环境信息(静态障碍物、动态障碍物预测),设定初始路径。
- 成本函数构建:定义一个成本函数,该函数考虑路径的平滑性、与障碍物的最小距离、与参考路径的接近程度等因素。
- 优化迭代:利用优化算法(如梯度下降、遗传算法、粒子群优化等)迭代调整路径参数,以最小化成本函数。
- 终止条件:当路径参数变化小于某个阈值或达到最大迭代次数时,结束迭代。
第二步:速度规划
- 参考速度:基于路径规划的结果,确定一个初步的速度参考,考虑道路限制和车辆动力学约束。
- 速度调整:考虑前一帧的速度规划信息和当前路径的特性,调整速度以确保车辆能够安全、平顺地行驶。
- 反馈机制:利用闭环控制策略,根据车辆的实际状态和路径规划结果调整速度,确保车辆能够按照规划的路径行驶。
3. 决策和交通规则约束
EM Planner在规划过程中严格遵守交通规则作为硬约束,确保规划出的路径合法且安全。同时,算法将与障碍物的交互视为软约束,通过动态调整路径和速度,以最小化与障碍物的潜在冲突。这一机制通过决策模块实现,它为整个规划过程提供了明确的意图和目标,从而缩小了最优求解的搜索空间,提高了规划的效率和准确性。
硬约束(Hard Constraints):
这些是必须遵守的规则,如交通信号、速度限制、道路边界等。硬约束确保了规划出的路径在法律和安全上是可行的。
<pre data-index="2" class="new-version hljs set-code-height set-code-hide" name="code"><code class="language-cpp hljs"><ol class="hljs-ln" style="width:100%"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-comment">// 确保车辆速度不超过最大限速的硬约束函数</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-function"><span class="hljs-type">bool</span> <span class="hljs-title">isSpeedWithinLimit</span><span class="hljs-params">(<span class="hljs-type">float</span> currentSpeed, <span class="hljs-type">float</span> maxSpeed)</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">return</span> currentSpeed <= maxSpeed;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="6"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-comment">// 确保车辆不越过交通信号灯的硬约束函数</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="7"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-function"><span class="hljs-type">bool</span> <span class="hljs-title">isAtTrafficLight</span><span class="hljs-params">(<span class="hljs-type">float</span> vehiclePosition, <span class="hljs-type">float</span> lightPosition)</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="8"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-comment">// 假设车辆在交通灯位置之前必须停下</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="9"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">return</span> vehiclePosition <= lightPosition;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="10"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li></ol></code><div class="opt-box"><button class="btn-code-notes ckeditor" style="background-image:url(https://i-operation.csdnimg.cn/images/a5fff6f6c9f0464c9a46b130c972952b.png);" data-report-view="{"spm":"3001.10436","dest":"https://trae.com.cn?utm_source=community&utm_medium=csdn&utm_campaign=daima","extra":{"index":2,"ab":"control"}}" data-report-click="{"spm":"3001.10436","dest":"https://trae.com.cn?utm_source=community&utm_medium=csdn&utm_campaign=daima","extra":{"index":2,"ab":"control"}}">AI写代码</button><span class="code-language" data-language="cpp">cpp</span><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div><span class="code-run-btn" data-idx="2" data-report-view="{"spm":"3001.10758","extra":{"language":"cpp", "idx":"2"}}" data-language="cpp">运行</span></div><div class="hide-preCode-box"><span class="hide-preCode-bt" data-report-view="{"spm":"1001.2101.3001.7365"}"><img class="look-more-preCode contentImg-no-view" src="https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png" alt="" title=""></span></div></pre> </li><li> <p><strong>软约束(Soft Constraints)</strong>:</p> 这些是建议性规则,如避免与障碍物的潜在冲突。软约束允许算法在不违反硬约束的情况下,对路径进行微调,以提高效率或舒适度。 <pre data-index="3" class="new-version hljs set-code-height set-code-hide" name="code"><code class="language-cpp hljs"><ol class="hljs-ln" style="width:882px"><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="1"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-comment">// 计算与障碍物的距离,作为软约束的函数</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-function"><span class="hljs-type">float</span> <span class="hljs-title">calculateObstacleDistance</span><span class="hljs-params">(<span class="hljs-type">float</span> vehiclePosition, <span class="hljs-type">const</span> std::vector<<span class="hljs-type">float</span>>& obstaclePositions)</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="3"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-type">float</span> minDistance = std::numeric_limits<<span class="hljs-type">float</span>>::<span class="hljs-built_in">max</span>();</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="4"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">for</span> (<span class="hljs-type">float</span> obstaclePosition : obstaclePositions) {</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="5"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> minDistance = std::<span class="hljs-built_in">min</span>(minDistance, std::<span class="hljs-built_in">fabs</span>(vehiclePosition - obstaclePosition));</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="6"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="7"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">return</span> minDistance;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="8"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="9"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> </div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="10"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-comment">// 计算车辆与行人的距离,作为软约束的函数</span></div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="11"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-function"><span class="hljs-type">float</span> <span class="hljs-title">calculatePedestrianDistance</span><span class="hljs-params">(<span class="hljs-type">float</span> vehiclePosition, <span class="hljs-type">const</span> std::vector<<span class="hljs-type">float</span>>& pedestrianPositions)</span> </span>{</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="12"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-type">float</span> minDistance = std::numeric_limits<<span class="hljs-type">float</span>>::<span class="hljs-built_in">max</span>();</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="13"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">for</span> (<span class="hljs-type">float</span> pedestrianPosition : pedestrianPositions) {</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="14"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> minDistance = std::<span class="hljs-built_in">min</span>(minDistance, std::<span class="hljs-built_in">fabs</span>(vehiclePosition - pedestrianPosition));</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="15"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> }</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="16"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"> <span class="hljs-keyword">return</span> minDistance;</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="17"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line">}</div></div></li></ol></code><div class="opt-box"><button class="btn-code-notes ckeditor" style="background-image:url(https://i-operation.csdnimg.cn/images/a5fff6f6c9f0464c9a46b130c972952b.png);" data-report-view="{"spm":"3001.10436","dest":"https://trae.com.cn?utm_source=community&utm_medium=csdn&utm_campaign=daima","extra":{"index":3,"ab":"control"}}" data-report-click="{"spm":"3001.10436","dest":"https://trae.com.cn?utm_source=community&utm_medium=csdn&utm_campaign=daima","extra":{"index":3,"ab":"control"}}">AI写代码</button><span class="code-language" data-language="cpp">cpp</span><div class="hljs-button {2}" data-title="复制" onclick="hljs.copyCode(event)"></div><span class="code-run-btn" data-idx="3" data-report-view="{"spm":"3001.10758","extra":{"language":"cpp", "idx":"3"}}" data-language="cpp">运行</span></div><div class="hide-preCode-box"><span class="hide-preCode-bt" data-report-view="{"spm":"1001.2101.3001.7365"}"><img class="look-more-preCode contentImg-no-view" src="https://csdnimg.cn/release/blogv2/dist/pc/img/runCode/icon-arrowwhite.png" alt="" title=""></span></div></pre> </li><li> <p><strong>动态调整(Dynamic Adjustment)</strong>:</p> 算法能够根据实时环境变化(如交通流量、障碍物出现等)动态调整路径和速度,以适应不断变化的条件。</li><li> <p><strong>决策模块(Decision Module)</strong>:</p> 这是算法的核心部分,负责制定策略和目标。决策模块通过分析当前环境和交通状况,确定最优的路径规划方案。</li><li> <p><strong>意图和目标(Intentions and Goals)</strong>:</p> 决策模块为规划过程设定明确的意图和目标,如尽快到达目的地、避免拥堵区域等,这有助于缩小搜索空间,提高算法的效率。</li><li> <p><strong>最优求解搜索空间(Optimal Solution Search Space)</strong>:</p> 通过设定意图和目标,算法可以排除那些不符合这些条件的潜在解决方案,从而减少需要考虑的选项,加快求解过程。</li><li> <p><strong>效率和准确性(Efficiency and Accuracy)</strong>:</p> 通过上述机制,EM Planner能够在保证安全和合法性的同时,提供高效和准确的路径规划结果。</li></ol></blockquote>
4. SL和ST投影
Frenet坐标系是一种用于自动驾驶车辆路径规划的坐标系统,它将道路表示为一系列沿着道路中心线的点,这些点的集合称为路径。在Frenet坐标系中,每个点由两个坐标表示: 表示沿路径的距离,而
表示垂直于路径的距离。EM Planner使用Frenet坐标系进行路径和速度规划,利用SL(纵向位置)和ST(速度时间)投影,在E-step(期望步)和M-step(最大化步)中处理障碍物信息。在E-step中,所有障碍物(包括静态和动态障碍物)被投影到车道Frenet坐标系中,以便于算法理解和处理。在M-step中,障碍物的位置和速度信息被映射到速度时间图(ST图)中,与车辆生成的速度信息进行比较和优化,从而确保规划出的轨迹能够安全地避免障碍物。
Cartesian坐标系到Frenet坐标系的转换
cpp
*
// 定义点结构体,用于存储二维坐标
*
struct Point {
*
double x; // x坐标
*
double y; // y坐标
*
};
*
*
// 定义Frenet结构体,用于存储Frenet坐标系下的s, l坐标和道路曲率
*
struct Frenet {
*
double s; // 沿路径的距离
*
double l; // 垂直于路径的距离,这里用l代替d
*
double yaw; // 道路曲率
*
};
*
*
// 函数实现
*
Frenet toFrenet(const Point& car_pos, const std::vector<Point>& map_waypoints) {
*
// 初始化变量
*
double min_l = std::numeric_limits<double>::infinity(); // 最小垂直距离初始化为无穷大
*
double closest_x, closest_y; // 最近点的x, y坐标
*
double s = 0; // 初始s坐标
*
double l = 0; // 初始l坐标
*
double closest_len = 0; // 最近点到车辆的距离
*
double prev_s = 0; // 上一个点的s坐标
*
double prev_yaw = 0; // 上一个点的yaw角度
*
*
// 遍历所有地图路径点
*
for (int i = 0; i < map_waypoints.size(); i++) {
*
// 计算当前点到车辆的x, y距离
*
double x = map_waypoints[i].x - car_pos.x;
*
double y = map_waypoints[i].y - car_pos.y;
*
*
// 计算当前点到车辆的距离
*
double dist = sqrt(x * x + y * y);
*
*
// 计算与前一个点的距离
*
double dist_to_prev = (i > 0) ?
*
sqrt((x - map_waypoints[i - 1].x) * (x - map_waypoints[i - 1].x) +
*
(y - map_waypoints[i - 1].y) * (y - map_waypoints[i - 1].y)) : 0;
*
*
// 更新s坐标
*
s += dist_to_prev;
*
*
// 如果当前点到车辆的距离小于已知的最小垂直距离,则更新最近点信息
*
if (dist < min_l) {
*
closest_x = x;
*
closest_y = y;
*
closest_len = dist;
*
min_l = dist;
*
}
*
*
// 如果不是第一个点,计算道路的曲率和切线角度
*
if (i > 0 && dist_to_prev > 0) {
*
// 计算当前点与前一个点的切线角度
*
double yaw = atan2(y, x);
*
prev_yaw = atan2(map_waypoints[i - 1].y - car_pos.y, map_waypoints[i - 1].x - car_pos.x);
*
// 计算道路曲率
*
prev_yaw = atan2(sin(prev_yaw - yaw), (cos(prev_yaw) * cos(yaw) + sin(prev_yaw) * sin(yaw)));
*
}
*
}
*
*
// 计算车辆到最近点的垂直距离l
*
l = sqrt(closest_x * closest_x + closest_y * closest_y) - closest_len;
*
*
// 计算车辆的切线角度,这里假设道路是直线,所以yaw为0
*
double final_yaw = 0;
*
*
// 返回Frenet坐标系下的坐标
*
return {s, l, final_yaw};
*
}
AI写代码cpp
运行

5. DP路径和QP路径
EM Planner采用**Dynamic Programming(动态规划)和Quadratic Programming(二次规划)**两种方法生成路径。首先,通过DP算法获得一个初步的、粗略的路径解,构建出一个大致的行驶轨迹。接着,算法使用基于QP的样条曲线生成光滑路径,通过优化路径的平滑性和连续性,提高车辆行驶的舒适度和安全性。这一过程确保了路径规划的高效性和路径的平滑性,是EM Planner算法中的关键步骤之一。
Dynamic Programming(动态规划)
初步路径生成:通过将整个路径规划问题分解为一系列较小的子问题,DP能够逐步构建出一个粗略的路径解。每个子问题只考虑从当前位置到目标位置的局部最优路径。
状态空间定义:在路径规划中,状态空间通常由车辆的位置和时间(或其他相关参数)定义。
递归关系:DP通过定义状态转移方程来找到从一个状态到另一个状态的最优路径。这种递归方法允许算法利用之前的计算结果来简化当前的计算。
边界条件:DP需要定义边界条件,即当车辆到达目标位置时的成本或路径。
优化计算:DP通过回溯过程找到全局最优解,这种方法在计算上可能比较耗时,但对于初步路径生成是有效的。
cpp
*
// 动态规划路径优化
*
std::pair<double, double> optimizePath(const VehicleState& vehicle, const std::vector<Obstacle>& obstacles, const std::vector<Obstacle>& map_waypoints) {
*
const int MAX_ITERATIONS = 100;
*
const double STEP_SIZE = 1.0; // 根据实际情况调整步长
*
const double THRESHOLD = 1e-6;
*
const double MAX_S = 100.0; // 最大s坐标值
*
const double MAX_L = 10.0; // 最大l坐标值
*
*
auto frenet_coordinates = toFrenet(vehicle, map_waypoints);
*
double s = frenet_coordinates.first;
*
double l = frenet_coordinates.second;
*
double cost = std::numeric_limits<double>::max();
*
*
std::vector<Eigen::Vector2d> control_points; // 用于多项式插值的控制点
*
*
for (int iter = 0; iter < MAX_ITERATIONS; ++iter) {
*
double prev_cost = cost;
*
for (double ds = -STEP_SIZE; ds <= STEP_SIZE; ds += STEP_SIZE) {
*
for (double dl = -STEP_SIZE; dl <= STEP_SIZE; dl += STEP_SIZE) {
*
double new_s = s + ds;
*
double new_l = l + dl;
*
*
// 添加边界条件检查
*
if (new_s < 0 || new_s > MAX_S || new_l < 0 || new_l > MAX_L) continue;
*
*
double new_cost = costFunction(vehicle, obstacles, new_s, new_l);
*
if (new_cost < cost) {
*
s = new_s;
*
l = new_l;
*
cost = new_cost;
*
control_points.push_back(Eigen::Vector2d(s, l)); // 收集控制点
*
}
*
}
*
}
*
*
if (fabs(prev_cost - cost) < THRESHOLD) {
*
break; // 如果成本变化小于阈值,则停止迭代
*
}
*
}
*
*
// 使用多项式插值进行平滑处理
*
std::vector<Eigen::Vector2d> smooth_path = generateSmoothPath(control_points);
*
*
// 从平滑后的路径中提取最终的s和l坐标
*
// 这里假设我们取平滑路径的最后一个点作为最终路径点
*
if (!smooth_path.empty()) {
*
Eigen::Vector2d final_point = smooth_path.back();
*
s = final_point.x();
*
l = final_point.y();
*
}
*
*
return {s, l};
*
}
*
*
// 计算成本函数
*
double costFunction(const VehicleState& vehicle, const std::vector<Obstacle>& obstacles, double s, double l) {
*
double cost = 0.0;
*
for (const Obstacle& obs : obstacles) {
*
double dist_to_obs = hypot(s - obs.s, l - obs.l);
*
cost += 1.0 / std::max(dist_to_obs, 1e-6); // 避免除零
*
}
*
return cost;
*
}
*
*
// 定义一个辅助函数,用于转换控制点到Eigen::Vector2d数组
*
Eigen::MatrixXd controlPointsToEigen(const std::vector<Eigen::Vector2d>& control_points) {
*
Eigen::MatrixXd eigen_control_points(control_points.size(), 2);
*
for (int i = 0; i < control_points.size(); ++i) {
*
eigen_control_points.row(i) << control_points[i].x(), control_points[i].y();
*
}
*
return eigen_control_points;
*
}
*
*
// 多项式插值函数实现
*
std::vector<Eigen::Vector2d> generateSmoothPath(const std::vector<Eigen::Vector2d>& control_points) {
*
// 将控制点转换为Eigen矩阵
*
Eigen::MatrixXd eigen_control_points = controlPointsToEigen(control_points);
*
*
// 定义样条参数
*
const double tension = 0.0; // 张紧参数,0表示自然样条
*
const double bias = 0.0; // 偏差参数,0表示无偏差
*
const double smoothness = 1.0; // 平滑度参数
*
*
// 创建三次样条曲线
*
Eigen::Spline3d spline(Eigen::Dynamic, tension, bias, smoothness);
*
*
// 设置样条的控制点
*
spline.setControlPoints(eigen_control_points.transpose());
*
*
// 定义插值点的数量,这里我们假设在最小和最大s坐标之间均匀采样
*
double min_s = eigen_control_points.col(0).minCoeff();
*
double max_s = eigen_control_points.col(0).maxCoeff();
*
const int num_points = 100; // 插值点的数量
*
std::vector<Eigen::Vector2d> smooth_path;
*
*
// 计算插值点
*
for (int i = 0; i <= num_points; ++i) {
*
double s = min_s + i * (max_s - min_s) / num_points;
*
Eigen::Vector3d point_on_spline = spline(s);
*
smooth_path.emplace_back(point_on_spline(0), point_on_spline(1));
*
}
*
*
return smooth_path;
*
}
AI写代码cpp
运行

Quadratic Programming(二次规划)
平滑路径生成:在DP生成的初步路径基础上,QP可以用来生成更加平滑和连续的路径。这通常涉及到最小化路径曲率或加速度变化。
目标函数:QP的目标函数通常是路径的平滑性度量,如路径的二阶导数的平方和,这代表了路径的曲率。
约束条件:QP的约束条件可能包括车辆动力学限制、避免碰撞的边界以及必须遵守的交通规则。
数值优化:QP问题通常使用数值优化方法求解,如梯度下降、牛顿法或内点法等。
实时性:与DP相比,QP可以更快地求解,使其适合于需要快速响应的实时路径规划场景。
cpp
*
// 速度规划函数,基于二次规划求解
*
double planSpeedWithQP(const VehicleState& vehicle, const std::pair<double, double>& frenet_position, const std::vector<Obstacle>& obstacles) {
*
const int N = 10; // 时间步数
*
const double TIME_STEP = 0.1; // 时间步长
*
const double MAX_SPEED = 30; // 最大速度
*
const double MIN_SPEED = 0; // 最小速度
*
const double MAX_ACCEL = 5; // 最大加速度
*
const double MIN_ACCEL = -5; // 最小加速度
*
*
// 定义QP问题的维度
*
const int n = N; // 状态变量数,即速度
*
const int m = 2 * N; // 约束数,每个时间步的上下限约束
*
*
// 定义QP问题的矩阵和向量
*
Eigen::MatrixXd P(n, n); // 目标函数的二次项系数矩阵,对角阵
*
Eigen::VectorXd q(n); // 目标函数的线性项系数向量
*
Eigen::MatrixXd A(m, n); // 约束矩阵
*
Eigen::VectorXd l(m); // 约束下限向量
*
Eigen::VectorXd u(m); // 约束上限向量
*
*
// 初始化QP矩阵和向量
*
P.setIdentity();
*
q.setZero();
*
A.setZero();
*
l.setZero();
*
u.setZero();
*
*
// 设置QP矩阵和向量
*
// 设置QP矩阵和向量
*
// 目标函数:最小化加速的平方
*
for (int i = 0; i < n - 1; ++i) {
*
P(i, i) = 1.0 / (TIME_STEP * TIME_STEP);
*
P(i + 1, i) = -2.0 / (TIME_STEP * TIME_STEP);
*
P(i + 1, i + 1) = 1.0 / (TIME_STEP * TIME_STEP);
*
}
*
*
// 约束:速度上下限
*
for (int i = 0; i < N; i++) {
*
A(i, i) = 1.0;
*
A(N + i, i) = -1.0;
*
l(i) = MIN_SPEED;
*
u(i) = MAX_SPEED;
*
l(N + i) = MIN_ACCEL;
*
u(N + i) = MAX_ACCEL;
*
}
*
*
// 设置初始状态
*
Eigen::VectorXd x0(n);
*
x0.setZero();
*
x0(0) = vehicle.v; // 当前速度
*
*
// 设置QP求解器
*
OSQPData data = {};
*
OSQPSettings settings = {};
*
osqp_setup(&settings, &data);
*
osqp_set_P(&settings, P.data());
*
osqp_set_A(&settings, A.data());
*
osqp_set_q(&settings, q.data());
*
osqp_set_l(&settings, l.data());
*
osqp_set_u(&settings, u.data());
*
osqp_update_settings(&settings, OSQP_DEFAULT);
*
osqp_setup(&data, &settings);
*
*
// 求解QP问题
*
osqp_solve(&data);
*
*
// 获取结果
*
double final_speed = x0(n - 1);
*
*
// 清理资源
*
osqp_cleanup(&data, &settings);
*
*
return final_speed;
*
}
AI写代码cpp
运行

6. 参考线轨迹决策
最后,参考线轨迹决策模块根据当前车辆状态、交通规则约束、障碍物信息以及每条轨迹的代价,综合评估并决定最优的行驶轨迹。这一决策过程考虑了车辆的动态特性、环境的动态变化以及交通规则的约束,确保了规划出的轨迹既符合安全标准,也满足舒适性和效率的要求。
cpp
*
// 决策模块中用于评估轨迹代价的函数
*
float evaluateTrajectoryCost(const Trajectory& trajectory, float maxSpeed, const std::vector<float>& obstacles) {
*
// 检查速度是否超过最大限速
*
for (float position : trajectory.positions) {
*
// 这里假设有一个函数calculateSpeed(position)来计算当前位置的速度
*
float speed = calculateSpeed(position); // 需要实现calculateSpeed函数
*
if (speed > maxSpeed) {
*
return std::numeric_limits<float>::infinity(); // 如果速度超过限制,返回无穷大代价
*
}
*
}
*
*
// 检查是否避开了所有障碍物
*
for (float obstacle : obstacles) {
*
for (float position : trajectory.positions) {
*
if (position == obstacle) { // 简化检查,实际可能需要更复杂的距离计算
*
return std::numeric_limits<float>::infinity(); // 如果轨迹与障碍物相交,返回无穷大代价
*
}
*
}
*
}
*
*
// 如果轨迹满足所有约束条件,返回一个合理的非无穷大代价
*
// 这里只是一个示例值,实际中应根据具体情况计算
*
return 1.0f; // 可以是任何合理的初始代价值
*
}
AI写代码cpp
运行

EM Planner算法与端到端自动驾驶的结合
将EM Planner算法与端到端自动驾驶技术结合,是自动驾驶领域迈向更高智能化水平的关键一步。EM Planner的精细路径规划与速度控制能力,与端到端技术的环境感知和决策制定优势互补,共同构建了一个既高效又安全的自动驾驶系统。这种结合不仅提高了系统的适应性和鲁棒性,还促进了自动驾驶从理论到实践的无缝衔接,为实现全自动驾驶的愿景提供了坚实的技术支撑。随着技术的不断进步和应用场景的拓展,EM Planner与端到端自动驾驶的融合将更加深入,有望引领自动驾驶领域的未来发展方向,为智慧出行创造更多可能。

</div><div data-report-view="{"mod":"1585297308_001","spm":"1001.2101.3001.6548","dest":"https://blog.csdn.net/LIUMAO99/article/details/141054445","extend1":"pc","ab":"new"}"><div></div></div>
</div>