Ego Planner规划算法(下)——Ego Planner程序框架

   本系列文章用于学习记录Ego-Planner规划算法的相关内容,本系列文章是在在Fast Planner规划算法系列文章的基础上,展开对Ego Planner的相关介绍,主要包括Ego Planner算法流程和程序框架两部分,主要学习资料是深蓝学院的移动机器人运动规划课程

   二、Ego Planner程序框架

   1、各功能包作用

   Ego Planner的开源代码中,uav_simulator文件夹中存放的是一些仿真相关的程序,planner文件夹中存放的是Ego Planner算法相关的程序。

   在planner文件夹中包含多个功能包,各功能包的作用如下:

   (1)、bspline_opt :均匀B样条,用于轨迹优化

   (2)、path_searching:dynamic A * 算法,但在Ego Planner的规划过程中并没有用到

   (3)、plan_env:用于地图生成

   (4)、plan_manage:主文件夹,主节点所在文件夹

   (5)、traj_utils:可视化及多项式轨迹类的相关内容


   2、Ego Planner程序框架/流程

   我们从主函数所在的ego_planner_node.cpp文件开始,该文件中初始化Ego Planner的节点以后,通过rebo_replan.init函数,来调用Ego Planner算法相关的程序,rebo_replan.init函数的具体程序在ego_replan_fsm.cpp文件中

rebo_replan.init(nh);
void EGOReplanFSM::init(ros::NodeHandle &nh)

   ego_replan_fsm.cpp文件中execFSMCallback函数是状态基,用于处理在机器人运行过程中遇到各种情况下,是选择急停、还是重新规划、还是继续执行等。

   我们前面介绍的Ego Planner算法的具体程序都在ego_replan_fsm.cpp 文件中 callReboundReplan函数中调用的reboundReplan函数中。

 bool EGOReplanFSM::callReboundReplan(bool flag_use_poly_init, bool flag_randomPolyTraj)
 bool plan_success =
        planner_manager_->reboundReplan(start_pt_, start_vel_, start_acc_, local_target_pt_, local_target_vel_, (have_new_target_ || flag_use_poly_init), flag_randomPolyTraj);

   reboundReplan函数的具体程序在planner_manager.cpp文件中

bool EGOPlannerManager::reboundReplan(Eigen::Vector3d start_pt, Eigen::Vector3d start_vel,
                                        Eigen::Vector3d start_acc, Eigen::Vector3d local_target_pt,
                                        Eigen::Vector3d local_target_vel, bool flag_polyInit, bool flag_randomPolyTraj)

   接下来,我们就顺着reboundReplan函数来看一下Ego Planner算法的流程

   首先是不考虑障碍物的情况下,生成从起点到目标点的光滑轨迹,如果之前已经有轨迹了,则初始轨迹就基于之前的轨迹来生成,这样保证在重规划时不会出现一会儿让无人机往左飞,一会儿让无人机往右飞,在几条轨迹间来回横跳的现象。

  double ts = (start_pt - local_target_pt).norm() > 0.1 ? pp_.ctrl_pt_dist / pp_.max_vel_ * 1.2 : pp_.ctrl_pt_dist / pp_.max_vel_ * 5; // pp_.ctrl_pt_dist / pp_.max_vel_ is too tense, and will surely exceed the acc/vel limits
    vector<Eigen::Vector3d> point_set, start_end_derivatives;
    static bool flag_first_call = true, flag_force_polynomial = false;
    bool flag_regenerate = false;
    do
    {
      point_set.clear();
      start_end_derivatives.clear();
      flag_regenerate = false;

      if (flag_first_call || flag_polyInit || flag_force_polynomial /*|| ( start_pt - local_target_pt ).norm() < 1.0*/) // Initial path generated from a min-snap traj by order.
      {

		 ......

       }
       else // Initial path generated from previous trajectory.
      {
      
		 ......

      }
     } while (flag_regenerate);

   上面生成的轨迹并不是B样条轨迹,接下来需要将其变成B样条轨迹(通过求解最小二乘问题获得B样条的控制带你来进行转换),该部分内容是通过调用parameterizeToBspline函数完成的,该函数的具体程序位于uniform_bspline.cpp文件中,该函数的输入参数中point_set是轨迹上的点,start_end_derivatives是起点和终点的高阶约束

UniformBspline::parameterizeToBspline(ts, point_set, start_end_derivatives, ctrl_pts);
  void UniformBspline::parameterizeToBspline(const double &ts, const vector<Eigen::Vector3d> &point_set,
                                             const vector<Eigen::Vector3d> &start_end_derivative,
                                             Eigen::MatrixXd &ctrl_pts)

   转换完成后,我们就得到了后端优化的初值,然后执行轨迹优化,该部分内容是通过调用BsplineOptimizeTrajRebound函数来完成的,该函数的具体程序在bspline_optimizer.cpp中

 /*** STEP 2: OPTIMIZE ***/
    bool flag_step_1_success = bspline_optimizer_rebound_->BsplineOptimizeTrajRebound(ctrl_pts, ts);
  bool BsplineOptimizer::BsplineOptimizeTrajRebound(Eigen::MatrixXd &optimal_points, double ts)

   优化结束后,开始执行时间重分配,该部分内容通过调用refineTrajAlgo函数来完成,该函数的具体程序位于planner_manager.cpp文件中

flag_step_2_success = refineTrajAlgo(pos, start_end_derivatives, ratio, ts, optimal_control_points);
bool EGOPlannerManager::refineTrajAlgo(UniformBspline &traj, vector<Eigen::Vector3d> &start_end_derivative, double ratio, double &ts, Eigen::MatrixXd &optimal_control_points)

   时间重分配流程结束后,就得到了最终的轨迹,将其发布出去


   3、重要函数的展开介绍(BsplineOptimizeTrajRebound和refineTrajAlgo)

   我们先来看,完成轨迹优化的BsplineOptimizeTrajRebound函数

   该函数的具体程序在bspline_optimizer.cpp中,首先将时间间隔ts传给B样条曲线,然后调用rebound_optimize函数执行轨迹优化

  bool BsplineOptimizer::BsplineOptimizeTrajRebound(Eigen::MatrixXd &optimal_points, double ts)
  {
    setBsplineInterval(ts);

    bool flag_success = rebound_optimize();

    optimal_points = cps_.points;

    return flag_success;
  }

   在rebound_optimize函数中,调用了LBFGS优化器进行优化

 int result = lbfgs::lbfgs_optimize(variable_num_, q, &final_cost, BsplineOptimizer::costFunctionRebound, NULL, BsplineOptimizer::earlyExit, this, &lbfgs_params);

   其核心是目标函数costFunctionRebound如何编写

  double BsplineOptimizer::costFunctionRebound(void *func_data, const double *x, double *grad, const int n)
  {
    BsplineOptimizer *opt = reinterpret_cast<BsplineOptimizer *>(func_data);

    double cost;
    opt->combineCostRebound(x, grad, cost, n);

    opt->iter_num_ += 1;
    return cost;
  }

   目标函数的具体表达式在costFunctionRebound函数调用的combineCostRebound函数中,该函数中又调用了calcSmoothnessCost、calcDistanceCostRebound、calcFeasibilityCost这三个函数,分别对应着目标函数中的平滑项、避障约束项、动力学约束项,其中calcDistanceCostRebound在计算避障约束项之前会先检测是否碰撞了障碍物,若是则会停止优化

 void BsplineOptimizer::combineCostRebound(const double *x, double *grad, double &f_combine, const int n)
  {

    memcpy(cps_.points.data() + 3 * order_, x, n * sizeof(x[0]));

    /* ---------- evaluate cost and gradient ---------- */
    double f_smoothness, f_distance, f_feasibility;

    Eigen::MatrixXd g_smoothness = Eigen::MatrixXd::Zero(3, cps_.size);
    Eigen::MatrixXd g_distance = Eigen::MatrixXd::Zero(3, cps_.size);
    Eigen::MatrixXd g_feasibility = Eigen::MatrixXd::Zero(3, cps_.size);

    calcSmoothnessCost(cps_.points, f_smoothness, g_smoothness);
    calcDistanceCostRebound(cps_.points, f_distance, g_distance, iter_num_, f_smoothness);
    calcFeasibilityCost(cps_.points, f_feasibility, g_feasibility);

    f_combine = lambda1_ * f_smoothness + new_lambda2_ * f_distance + lambda3_ * f_feasibility;
    //printf("origin %f %f %f %f\n", f_smoothness, f_distance, f_feasibility, f_combine);

    Eigen::MatrixXd grad_3D = lambda1_ * g_smoothness + new_lambda2_ * g_distance + lambda3_ * g_feasibility;
    memcpy(grad, grad_3D.data() + 3 * order_, n * sizeof(grad[0]));
  }

   我们再来看一下,用于时间重分配的refineTrajAlgo函数

   首先要计算出新的合适的时间间隔,该部分内容在reparamBspline函数中完成,然后基于新的时间间隔计算新轨迹的初值,该部分内容主要在UniformBspline函数中完成,然后,再对新的轨迹进行优化,该部分内容在BsplineOptimizeTrajRefine中完成

bool EGOPlannerManager::refineTrajAlgo(UniformBspline &traj, vector<Eigen::Vector3d> &start_end_derivative, double ratio, double &ts, Eigen::MatrixXd &optimal_control_points)
  {
    double t_inc;

    Eigen::MatrixXd ctrl_pts; // = traj.getControlPoint()

    // std::cout << "ratio: " << ratio << std::endl;
    reparamBspline(traj, start_end_derivative, ratio, ctrl_pts, ts, t_inc);

    traj = UniformBspline(ctrl_pts, 3, ts);

    double t_step = traj.getTimeSum() / (ctrl_pts.cols() - 3);
    bspline_optimizer_rebound_->ref_pts_.clear();
    for (double t = 0; t < traj.getTimeSum() + 1e-4; t += t_step)
      bspline_optimizer_rebound_->ref_pts_.push_back(traj.evaluateDeBoorT(t));

    bool success = bspline_optimizer_rebound_->BsplineOptimizeTrajRefine(ctrl_pts, ts, optimal_control_points);

    return success;
  }

   在BsplineOptimizeTrajRefine函数中,调用了refine_optimize函数来执行具体的优化过程。

  bool BsplineOptimizer::BsplineOptimizeTrajRefine(const Eigen::MatrixXd &init_points, const double ts, Eigen::MatrixXd &optimal_points)
  {

    setControlPoints(init_points);
    setBsplineInterval(ts);

    bool flag_success = refine_optimize();

    optimal_points = cps_.points;

    return flag_success;
  }

   refine_optimize函数中同样是调用了LBFGS求解器来就行优化过程,其核心依然是目标函数costFunctionRefine的给定

  bool BsplineOptimizer::refine_optimize()
int result = lbfgs::lbfgs_optimize(variable_num_, q, &final_cost, BsplineOptimizer::costFunctionRefine, NULL, NULL, this, &lbfgs_params);

   costFunctionRefine函数中调用了combineCostRefine函数来计算目标函数值

  double BsplineOptimizer::costFunctionRefine(void *func_data, const double *x, double *grad, const int n)
  {
    BsplineOptimizer *opt = reinterpret_cast<BsplineOptimizer *>(func_data);

    double cost;
    opt->combineCostRefine(x, grad, cost, n);

    opt->iter_num_ += 1;
    return cost;
  }

   combineCostRefine函数中又调用了calcSmoothnessCost函数、calcFitnessCost函数、calcFeasibilityCost函数来分别计算平滑项、曲线拟合项、动力学约束项

  void BsplineOptimizer::combineCostRefine(const double *x, double *grad, double &f_combine, const int n)
  {

    memcpy(cps_.points.data() + 3 * order_, x, n * sizeof(x[0]));

    /* ---------- evaluate cost and gradient ---------- */
    double f_smoothness, f_fitness, f_feasibility;

    Eigen::MatrixXd g_smoothness = Eigen::MatrixXd::Zero(3, cps_.points.cols());
    Eigen::MatrixXd g_fitness = Eigen::MatrixXd::Zero(3, cps_.points.cols());
    Eigen::MatrixXd g_feasibility = Eigen::MatrixXd::Zero(3, cps_.points.cols());

    //time_satrt = ros::Time::now();

    calcSmoothnessCost(cps_.points, f_smoothness, g_smoothness);
    calcFitnessCost(cps_.points, f_fitness, g_fitness);
    calcFeasibilityCost(cps_.points, f_feasibility, g_feasibility);

    /* ---------- convert to solver format...---------- */
    f_combine = lambda1_ * f_smoothness + lambda4_ * f_fitness + lambda3_ * f_feasibility;
    // printf("origin %f %f %f %f\n", f_smoothness, f_fitness, f_feasibility, f_combine);

    Eigen::MatrixXd grad_3D = lambda1_ * g_smoothness + lambda4_ * g_fitness + lambda3_ * g_feasibility;
    memcpy(grad, grad_3D.data() + 3 * order_, n * sizeof(grad[0]));
  }

   参考资料:

   1、深蓝学院-移动机器人运动规划


### EGO Planner 在无人车中的应用与实现 EGO Planner 是一种高效的路径规划算法,尤其适合于需要高精度避障和动态适应性的场景,如无人机、无人驾驶车辆等[^1]。以下是关于如何在自动驾驶领域中利用 EGO Planner 进行路径规划的具体分析。 #### 核心原理 EGO Planner 的核心优势在于能够在复杂环境中快速生成最优路径并实现实时调整。该算法通常结合局部感知数据(例如激光雷达或摄像头获取的信息),并通过优化目标函数来计算最佳轨迹。这种特性使其非常适合用于无人车的动态环境导航需求。 #### 规划流程概述 在一个典型的 ROS 平台下,EGO Planner 可以按照如下方式集成到无人车系统中: 1. **传感器数据融合** 使用 LiDAR 或其他传感器采集周围环境的数据,并将其转换为占用栅格地图或其他形式的地图表示。这些地图可以作为输入传递给 EGO Planner 以便进行路径决策。 2. **全局路径规划** 利用 A* 或 Dijkstra 等传统图搜索算法预先定义一条粗略的全局路径。这条路径会指导后续更精细的局部路径规划过程。 3. **局部路径优化** 基于当前状态信息(位置、速度、加速度等)以及最新的传感数据,调用 EGO Planner 来重新评估最近区域内的可行性和安全性。此阶段涉及复杂的数学建模和技术细节处理,比如碰撞检测、曲率约束满足等问题。 4. **控制命令发布** 将最终得到的最佳轨迹转化为具体的转向角、油门开度等物理量指令发送至执行机构完成动作输出。 #### 技术对比:FAST-Planner vs. EGO-Planner 值得注意的是,虽然两者都属于现代先进路径规划工具集成员之一,但它们之间存在显著差异。相比于 FAST-Planner 更注重效率方面表现——能在极短时间内完成高质量路径设计任务;而 EGO Planner 则倾向于综合考量多种因素达到平衡效果,在某些特定条件下可能展现出更好的灵活性和鲁棒性特点[^3]。 下面给出一段简单的 Python 示例代码片段演示如何初始化一个基本版本的 EGO Planner 对象: ```python from ego_planner import EgoPlanner def initialize_ego_planner(): planner = EgoPlanner() planner.set_map_parameters(width=10, height=10, resolution=0.1) start_pose = (0, 0, 0) # 起始姿态(x,y,theta) goal_pose = (9, 9, 0) # 终点姿态(x,y,theta) path = planner.plan(start_pose=start_pose, goal_pose=goal_pose) return path ``` 上述代码仅作为一个简化版示意图供参考学习之用,请根据实际情况调整参数配置等内容后再应用于真实项目开发当中去。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

慕羽★

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值