代码解析之自行车模型在Apollo规划中的应用

大家好,我已经把CSDN上的博客迁移到了知乎上,欢迎大家在知乎关注我的专栏慢慢悠悠小马车https://zhuanlan.zhihu.com/duangduangduang。希望大家可以多多交流,互相学习。


在进行无人车的轨迹规划时,需要考虑无人车的车辆模型,才可以规划出符合车辆运动特性的、舒适的、容易被跟踪的路径。常用的车辆运动学模型有自行车模型和阿克曼转向几何模型,自行车模型实际上是对阿克曼转向几何的一个简化。

我在之前分析Apollo Hybrid A* 算法时,提到了它使用自行车模型,在当前状态的基础上,计算下一步可行驶的状态,即 Baidu Apollo代码解析之Open Space Planner中的Hybrid A* 这篇博客中的下列代码。对于车辆状态(x, y, phi),x和y的推算很简单,这里讲一下phi的推算的依据。

//扩展节点,扩展一个node就是扩展了一个grid,但是会产生多个在同一grid内的路径点
std::shared_ptr<Node3d> HybridAStar::Next_node_generator(
    std::shared_ptr<Node3d> current_node, size_t next_node_index) {
  double steering = 0.0;
  size_t index = 0;
  double traveled_distance = 0.0;
  //steering angle为什么这么算?
  //首先,根据next_node_index与next_node_num_的对比是可以区分运动方向的
  //这里的if-else就是区分运动正反方向讨论的(前进和倒车)
  //其次,车辆在当前的姿态下,既可以向左转、又可以向右转,那么steering angle的
  //取值范围其实是[-max_steer_angle_, max_steer_angle_],在正向或反向下,
  //能取next_node_num_/2个有效值。
  //即,把[-max_steer_angle_, max_steer_angle_]分为(next_node_num_/2-1)份
  //所以,steering = 初始偏移量 + 单位间隔 × index
  //steering angle的正负取决于车的转向,而非前进的正反
  if (next_node_index < static_cast<double>(next_node_num_) / 2) {
    steering = -max_steer_angle_ +
        (2 * max_steer_angle_ / (static_cast<double>(next_node_num_) / 2 - 1)) *
            static_cast<double>(next_node_index);
    traveled_distance = step_size_;
  } else {
    index = next_node_index - next_node_num_ / 2;
    steering = -max_steer_angle_ +
        (2 * max_steer_angle_ / (static_cast<double>(next_node_num_) / 2 - 1)) *
            static_cast<double>(index);
    traveled_distance = -step_size_;
  }
  ...
  //从当前grid前进到下一个grid,一个grid内可能有多个路径点
  for (size_t i = 0; i < arc / step_size_; ++i) {
    const double next_x = last_x + traveled_distance * std::cos(last_phi);
    const double next_y = last_y + traveled_distance * std::sin(last_phi);
    //看车辆运动学模型——自行车模型
    const double next_phi = common::math::NormalizeAngle(last_phi +
        traveled_distance / vehicle_param_.wheel_base() * std::tan(steering));
    ...
  }
  ...
}

本文主要参考了 Apollo代码学习(二)—车辆运动学模型 和 无人驾驶汽车系统入门(五)——运动学自行车模型和动力学自行车模型 2篇博客。同时,推荐一篇论文《基于多传感器多路径规划自动泊车系统仿真及实车验证》便于理解。

1. 代码中的steering是什么含义?

steering的计算思路我在代码注释中分析过了,steering由max_steer_angle_ 推算而来,因此首先要理解max_steer_angle_ 是什么。max_steer_angle_ 在HybridAStar类的构造函数中初始化。

在自行车模型中,假设后轮朝向与车体朝向始终相同,因此,相对于车体的后轮转角恒等于0,由前轮控制、影响车辆的朝向,也就是说,转动方向盘导致的方向盘转角(即通常意义上的steer)变化,都反映到前轮转角(相对于车体)的变化上了,再由前轮转角影响到车体朝向(phi)。那么,方向盘转角和前轮转角的量化关系是怎样的呢?在Apollo中,假设了该量化关系是线性的(或许是在某种条件下的近似),即前轮转角 = 方向盘转角 / 某个比例。对应max_steer_angle_ 的赋值代码来看,vehicle_param_.max_steer_angle() 就是车辆允许的最大方向盘转角,vehicle_param_.steer_ratio() 就是线性关系的比例参数,而max_steer_angle_ 就是车辆允许的最大前轮转角。综上,steering是车辆的前轮转角大小。

HybridAStar::HybridAStar(const PlannerOpenSpaceConfig& open_space_conf) {
  ...
  max_steer_angle_ = vehicle_param_.max_steer_angle() / vehicle_param_.steer_ratio();
  ...
}

2. 为什么下一个状态的车体朝向这样计算?

抛开NormalizeAngle()不谈,代码中 新的朝向 = 旧的朝向 + 行驶距离 / 轴距 * tan(前轮转角)。这个式子的依据是什么呢?

const double next_phi = common::math::NormalizeAngle(
        last_phi + traveled_distance / vehicle_param_.wheel_base() * std::tan(steering));

假设短时间内,车辆的角速度是恒定的,则 next\_phi = last\_phi + \omega * dt 。而角速度 \omega = \frac{v}{R} 。则 

\omega * dt = \frac{v * dt}{R} =行驶距离 / 转弯半径

如图1,以后轴中心为参考基准,在直角三角形 O-前轮-后轮 中,\tan(\delta_{f}) = \frac{L}{R} \Rightarrow \frac{1}{R} = \frac{\tan(\delta_{f})}{L}。那么 \omega * dt = \frac{v * dt}{R} = \frac{v * dt * \tan(\delta_{f})}{L}next_phi = last_phi + 行驶距离 / 轴距 * tan(前轮转角),得证。

图1 以后轴中心为基准的自行车模型

3. 参考文章2中的车体朝向计算方式与此不同,怎么理解?

文章 无人驾驶汽车系统入门(五)——运动学自行车模型和动力学自行车模型 中,以车辆质心为参考基准,如图2所示。其(x, y, phi)的更新方式如图3所示。

图2 以车体质心为基准的自行车模型
图3 参考文章2中的状态计算

此时,next\_phi = last\_phi + \omega * dt 和 w * dt = v * dt / R = 行驶距离 / 转弯半径 依然成立。不同之处在于如何求R。在图2左侧直角三角形 O-后轮-质心 中,\sin\beta = \frac{L_{r}}{R} \Rightarrow \frac{1}{R} = \frac{\sin\beta }{L_{r}}。那么 next_phi = last_phi + v * dt * sin(β) / Lr = last_phi + 行驶距离 / Lr * sin(β)

4. 2种不同的计算方式的区别有多大?

对比2个式子,不同之处在于 行驶距离 / 轴距 * tan(前轮转角) 与 行驶距离 / Lr * sin(β)。对比图1和图2,当 L << R 时,前轮转角与β都会很小,tan(前轮转角)  ≈ 前轮转角sin(β) ≈ β,且 前轮转角 ≈ 2*β轴距 ≈ 2*Lr。二者几乎相等。

5. 题外话——向心加速度

Apollo在 Lattice Planner 中会对采样构造的横纵向轨迹进行评估,其中一项是计算向心加速度,代码如下。其实向心加速度就是车辆的横向加速度。那么

a_{y} = \frac{\text{d}v_{y}}{\text{d}t} = \frac{\text{d}(vsin\theta)}{\text{d}t} = v\frac{\text{d}(sin\theta)}{\text{d}t}

假设车辆偏离参考线较小,即 \theta 较小,则 \sin\theta \approx \theta。那么

a_{y} = v\frac{\text{d}\theta}{\text{d}t} = vw = v\frac{v}{R} =\frac{v^{2}}{R} = v^{2} * curvature

因为车辆偏离参考线较小,代码中使用参考线映射点的曲率来近似自车所在位置的曲率,即 ref_point.kappa()。

double TrajectoryEvaluator::CentripetalAccelerationCost(
    const PtrTrajectory1d& lon_trajectory) const {
  // Assumes the vehicle is not obviously deviate from the reference line.
  ...
  for (...) {
    double s = lon_trajectory->Evaluate(0, t);
    double v = lon_trajectory->Evaluate(1, t);
    PathPoint ref_point = PathMatcher::MatchToPath(*reference_line_, s);
    CHECK(ref_point.has_kappa());
    double centripetal_acc = v * v * ref_point.kappa();
    ...
  }
  ...
}

 

 

 

 

 

 

 

  • 6
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Apollo Planning决策规划算法在无人驾驶领域被广泛应用,在自动驾驶车辆起着至关重要的作用。该算法主要通过对车辆周围环境的感知和分析,实现智能驾驶路线的规划和决策,确保车辆安全行驶。 该算法的代码主要包含三个部分:感知模块、规划模块和控制模块。其感知模块主要负责采集车辆周围的环境信息,包括车辆所处的位置、路况、障碍物等。规划模块通过对这些信息的分析,提出一系列可能的驾驶路线,并通过评估这些路线的优劣来选择最佳驾驶路线。控制模块负责实现规划模块选择的最佳驾驶路线,并控制车辆按照路线行驶。 在Apollo Planning决策规划算法规划模块是实现驾驶决策的最重要模块,也是最具技术难度的模块。规划模块通过对车辆当前状态和环境信息的分析,提出一系列汽车驾驶路线。该算法采用在线生成路线方案的方法,路线生成的步骤如下: 1. 动态路径规划:根据车辆的位置和行驶状态,实时选择当前最佳的驾驶路线。 2. 静态路线生成:基于当前车辆所处的环境信息,生成固定的驾驶路线。 3. 组合路径规划:将动态路径规划和静态路线生成相结合,生成最终的驾驶路线。 除此之外,Apollo Planning决策规划算法还包括计算机视觉、机器学习、深度学习和人工智能等技术,这些技术的综合应用使得Apollo Planning决策规划算法成为无人驾驶领域应用最广泛的决策规划算法。 ### 回答2: Apollo Planning决策规划算法是一种用于自动驾驶系统的规划算法。该算法的主要作用是实时生成安全、有效且符合路况的路径以实现自动驾驶功能。本文将对该算法进行详细解析Apollo Planning决策规划算法主要包括三个步骤:路线规划运动规划和决策规划。具体代码如下: 1. 路线规划 ```c++ bool Planning::PlanOnReferenceLine() { std::vector<const hdmap::HDMap*> hdmap_vec; hdmap_vec.reserve(1); if (!GetHdmapOnRouting(current_routing_, &hdmap_vec)) { AERROR << "Failed to get hdmap on current routing with " << current_routing_.ShortDebugString(); return false; } const auto& reference_line_info = reference_line_infos_.front(); std::vector<ReferencePoint> ref_points; if (!CreateReferenceLineInfo(hdmap_vec.front(), reference_line_info, &ref_points)) { AERROR << "Failed to create reference line from routing"; return false; } // Smooth reference line Spline2d smoothed_ref_line; std::vector<double> s_refs; std::vector<double> l_refs; std::vector<double> headings; std::vector<double> kappas; std::vector<double> dkappas; if (!SmoothReferenceLine(ref_points, &smoothed_ref_line, &s_refs, &l_refs, &headings, &kappas, &dkappas)) { AERROR << "Failed to smooth reference line"; return false; } reference_line_info.SetTrajectory(&smoothed_ref_line); reference_line_info.SetReferenceLine(&ref_points); // set origin point if (!reference_line_info.SLToXY(s_refs.front(), 0.0, &origin_point_)) { AERROR << "Failed to get origin point on reference line"; return false; } return true; } ``` 在路线规划阶段Apollo Planning决策规划算法首先获取当前行驶路线和高精度地图数据。然后根据行驶路线和地图数据构建参考线,对参考线进行平滑处理,得到平滑后的参考线。此时我们可以得到平滑后的参考线的位置、方向和曲率等信息,这些信息将作为后面的运动和决策规划的输入。 2. 运动规划 ```c++ bool Planning::PlanOnPrediction() { PredictionObstacles prediction_obstacles; if (!GetPrediction(&prediction_obstacles)) { AERROR << "Prediction failed"; return false; } std::vector<Obstacle> obstacles; if (!BuildObstacle(prediction_obstacles, &obstacles)) { AERROR << "Unable to build obstacle"; return false; } const auto& reference_line_info = reference_line_infos_.front(); const auto& reference_line = reference_line_info.reference_line(); SpeedData speed_data; Cruiser::PlanningTarget planning_target; Status status = cruiser_->Plan(reference_line_info, obstacles, 0.0, reference_line.Length(), &speed_data, &planning_target); if (status != Status::OK()) { AERROR << "Failed to plan path with status: " << status; return false; } RecordDebugInfo(reference_line_info, obstacles, speed_data); return true; } ``` 运动规划主要用于生成车辆在参考线上的运行轨迹。在运动规划阶段,Apollo Planning决策规划算法首先获取预测障碍物信息,将预测的障碍物转化为Obstacle对象。然后根据当前平滑后的参考线、障碍物等信息进行运动规划运动规划的目标是生成符合规划目标的速度曲线。最后,Apollo Planning决策规划算法记录调试信息,以便后续分析调试。 3. 决策规划 ```c++ bool Planning::MakeDecision() { const auto& reference_line_info = reference_line_infos_.front(); const auto& reference_line = reference_line_info.reference_line(); std::vector<const Obstacle*> obstacles; if (!Obstacle::CreateObstacleRegions(FLAGS_max_distance_obstacle, reference_line_info, &obstacles)) { AERROR << "Failed to create obstacle regions"; return false; } for (auto obstacle_ptr : obstacles) { const auto& obstacle = *obstacle_ptr; if (obstacle.IsVirtual()) { continue; } if (obstacle.IsStatic()) { continue; } if (obstacle.type() == PerceptionObstacle::BICYCLE || obstacle.type() == PerceptionObstacle::PEDESTRIAN) { continue; } const auto& nearest_path_point = obstacle.nearest_point(); const SLPoint obstacle_sl = reference_line_info.xy_to_sl(nearest_path_point); if (obstacle_sl.s() < -FLAGS_max_distance_obstacle || obstacle_sl.s() > reference_line.Length() + FLAGS_max_distance_obstacle) { continue; } ObjectDecisionType decision; decision.mutable_avoid(); decision.mutable_avoid()->set_distance_s(-obstacle_sl.s()); reference_line_info.AddCost(&obstacle, &decision); } std::vector<ObjectDecisionType> decisions; if (!traffic_rule_manager_.ApplyRule(reference_line_info, &decisions)) { AERROR << "Failed to apply traffic rule manager"; return false; } reference_line_info.SetDecision(decisions); return true; } ``` 决策规划是基于当前环境信息和规划的路径等输入信息,实时生成控制命令的过程。在Apollo Planning决策规划算法,决策规划阶段根据当前参考线、障碍物等信息生成决策。该算法根据不同的规则和策略,生成不同的控制命令,以保证车辆安全、有效地运行。 综上,Apollo Planning决策规划算法是自动驾驶系统重要的规划算法之一,它通过路线规划运动规划和决策规划三个步骤,实现了安全、有效和符合路况的路径规划,为自动驾驶车辆的控制提供了重要的支持。 ### 回答3: Apollo Planning(阿波罗规划)是百度自动驾驶平台Apollo的一种决策规划算法,主要用于规划车辆的驾驶行为。该算法基于深度强化学习,使用了运动学模型和环境感知技术,可以根据车辆当前位置和目的地,生成一条最优的行驶路径,包括车辆的控制指令和行驶速度等。 该算法的核心技术是深度强化学习,它通过对驾驶过程进行大量的仿真,让计算机通过自我学习得到驾驶规则,使车辆能够根据不同的场景做出最优的决策。具体而言,算法先通过神经网络生成一系列潜在的行动策略,然后通过与环境进行交互、执行行动并接收环境反馈来评估每个策略的优劣,最终选取最优策略进行执行。 在实现上,Apollo Planning算法主要由四个模块构成:感知模块、规划模块、执行模块和控制模块。感知模块主要用于获取车辆周围环境的信息,包括车辆位置、速度、道路情况、交通灯等;规划模块根据感知模块提供的信息和车辆的目的地,生成一条最优的行驶路径;执行模块则根据规划模块生成的路径信息,实现车辆的自主驾驶;控制模块则根据执行模块生成的控制指令,控制车辆的加速、刹车、转向等行为。 在算法实现上,Apollo Planning采用了C++编程语言,结合ROS框架实现各模块之间的数据交互和代码复用,保证了算法的高效性和可维护性。算法代码实现方面还采用了许多优化技术,包括多线程并发执行、高效的数据结构和算法等,以提升算法的运行效率和稳定性。 总之,Apollo Planning是一种基于深度强化学习的决策规划算法,具有高效、自主、可靠等特点,在智能驾驶领域具有广泛应用前景。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值