当前位置:   article > 正文

FAST PLANNER代码阅读笔记_fast planner github

fast planner github

一、代码运行

首先挂上github链接

https://github.com/HKUST-Aerial-Robotics/Fast-Plannerhttps://github.com/HKUST-Aerial-Robotics/Fast-Planner前端采用Kinodynamic_Astar进行搜索路径,后端采用--优化轨迹。

运行方法:

首先下载功能包,放到src下面,然后catkin_make可以直接进行编译,我用的是Ubuntu18.04。

然后分别在两个终端运行下面两句代码,就可以启动rviz,然后使用2D Nav Goal给它终点坐标即可规划。

  1. source devel/setup.bash && roslaunch plan_manage rviz.launch
  2. source devel/setup.bash && roslaunch plan_manage kino_replan.launch

遇到的报错:pcl-ros没有安装,安装即可

二、代码阅读

这个项目代码量非常大,我是按照功能包的实现顺序来阅读

1、规划管理器plan_manage

这个功能包主要实现前端的kinoastar规划:

fast_planner_node.cpp这个文件主要用来启动ROS节点,然后进行初始化

  1. int main(int argc, char** argv) {
  2. ros::init(argc, argv, "fast_planner_node");
  3. ros::NodeHandle nh("~"); //创建一个节点句柄,命名为~
  4. int planner;
  5. nh.param("planner_node/planner", planner, -1);
  6. TopoReplanFSM topo_replan;
  7. KinoReplanFSM kino_replan;
  8. if (planner == 1) {//planner默认是1
  9. kino_replan.init(nh);
  10. } else if (planner == 2) {
  11. topo_replan.init(nh);
  12. }
  13. ros::Duration(1.0).sleep();
  14. ros::spin();
  15. return 0;
  16. }

接下来看KinoReplanFSM类以及其init函数

  1. void KinoReplanFSM::init(ros::NodeHandle& nh) {
  2. current_wp_ = 0;
  3. exec_state_ = FSM_EXEC_STATE::INIT;
  4. have_target_ = false;
  5. have_odom_ = false;
  6. /* fsm param */
  7. nh.param("fsm/flight_type", target_type_, -1);
  8. nh.param("fsm/thresh_replan", replan_thresh_, -1.0);
  9. nh.param("fsm/thresh_no_replan", no_replan_thresh_, -1.0);
  10. nh.param("fsm/waypoint_num", waypoint_num_, -1);
  11. for (int i = 0; i < waypoint_num_; i++) {
  12. nh.param("fsm/waypoint" + to_string(i) + "_x", waypoints_[i][0], -1.0);
  13. nh.param("fsm/waypoint" + to_string(i) + "_y", waypoints_[i][1], -1.0);
  14. nh.param("fsm/waypoint" + to_string(i) + "_z", waypoints_[i][2], -1.0);
  15. }
  16. /* initialize main modules */
  17. planner_manager_.reset(new FastPlannerManager);
  18. planner_manager_->initPlanModules(nh);
  19. visualization_.reset(new PlanningVisualization(nh));
  20. /* callback */
  21. // 创建一个定时器,以某一Duration 秒 的时间间隔执行某一回调函数
  22. exec_timer_ = nh.createTimer(ros::Duration(0.01), &KinoReplanFSM::execFSMCallback, this);
  23. safety_timer_ = nh.createTimer(ros::Duration(0.05), &KinoReplanFSM::checkCollisionCallback, this);
  24. waypoint_sub_ =
  25. nh.subscribe("/waypoint_generator/waypoints", 1, &KinoReplanFSM::waypointCallback, this);
  26. odom_sub_ = nh.subscribe("/odom_world", 1, &KinoReplanFSM::odometryCallback, this);
  27. replan_pub_ = nh.advertise<std_msgs::Empty>("/planning/replan", 10);
  28. new_pub_ = nh.advertise<std_msgs::Empty>("/planning/new", 10);
  29. bspline_pub_ = nh.advertise<plan_manage::Bspline>("/planning/bspline", 10);
  30. }

init函数,以一定的时间间隔调用函数execFSMCallback和checkCollisionCallback。

execFSMCallback函数用来实现对于各种状态的管理

execFSMCallback根据不同状态使用switch语句,对每个状态进行管理,其中重点在GEN_NEW_TRAJ,EXEC_TRAJ和REPLAN_TRAJ

  1. case GEN_NEW_TRAJ: {
  2. start_pt_ = odom_pos_;
  3. start_vel_ = odom_vel_;
  4. start_acc_.setZero();
  5. Eigen::Vector3d rot_x = odom_orient_.toRotationMatrix().block(0, 0, 3, 1);
  6. start_yaw_(0) = atan2(rot_x(1), rot_x(0));
  7. start_yaw_(1) = start_yaw_(2) = 0.0;
  8. bool success = callKinodynamicReplan();
  9. if (success) {
  10. changeFSMExecState(EXEC_TRAJ, "FSM"); // 如果成功获取一条轨迹,就改状态为EXEC_TRAJ,去执行
  11. } else {
  12. // have_target_ = false;
  13. // changeFSMExecState(WAIT_TARGET, "FSM");
  14. changeFSMExecState(GEN_NEW_TRAJ, "FSM"); // 如果没成功,就继续保持生成状态
  15. }
  16. break;

2、KinodynamicAstar规划

(1)callKinodynamicReplan函数

GEN_NEW_TRAJ中主要语句是

bool success = callKinodynamicReplan();

这句话调用了callKinodynamicReplan函数

  1. bool KinoReplanFSM::callKinodynamicReplan() {
  2. bool plan_success =
  3. planner_manager_->kinodynamicReplan(start_pt_, start_vel_, start_acc_, end_pt_, end_vel_);

然后紧接着调用kinodynamicReplan函数,这个是重点的规划函数

函数输入起点,起始速度,起始加速度,终点位置,终点速度

kinodynamicReplan的主要语句是

  int status = kino_path_finder_->search(start_pt, start_vel, start_acc, end_pt, end_vel, true);

调用了这个search函数,用来进行kinodynamic_Astar规划

(2)搜索轨迹函数KinodynamicAstar::search

重点函数:KinodynamicAstar::search

  1. int KinodynamicAstar::search(Eigen::Vector3d start_pt, Eigen::Vector3d start_v, Eigen::Vector3d start_a,
  2. Eigen::Vector3d end_pt, Eigen::Vector3d end_v, bool init, bool dynamic, double time_start)
  3. {
  4. start_vel_ = start_v;
  5. start_acc_ = start_a;
  6. PathNodePtr cur_node = path_node_pool_[0];
  7. cur_node->parent = NULL;
  8. // 一个state是一个6×1的向量,前面三个存位置,后面三个存速度信息
  9. cur_node->state.head(3) = start_pt;
  10. cur_node->state.tail(3) = start_v;
  11. cur_node->index = posToIndex(start_pt);
  12. cur_node->g_score = 0.0;
  13. Eigen::VectorXd end_state(6);
  14. Eigen::Vector3i end_index;
  15. double time_to_goal;
  16. end_state.head(3) = end_pt;
  17. end_state.tail(3) = end_v;
  18. end_index = posToIndex(end_pt);
  19. cur_node->f_score = lambda_heu_ * estimateHeuristic(cur_node->state, end_state, time_to_goal);
  20. cur_node->node_state = IN_OPEN_SET; // open_set_为优先级队列,比较的为 PathNodePtr 的fScore
  21. open_set_.push(cur_node);
  22. use_node_num_ += 1;
  23. // 考虑时间实时规划
  24. if (dynamic)
  25. {
  26. time_origin_ = time_start;
  27. cur_node->time = time_start;
  28. cur_node->time_idx = timeToIndex(time_start); // 考虑时间因素的话,就选择带有时间信息的insert
  29. expanded_nodes_.insert(cur_node->index, cur_node->time_idx, cur_node);
  30. // cout << "time start: " << time_start << endl;
  31. }
  32. else //否则采用普通insert
  33. expanded_nodes_.insert(cur_node->index, cur_node);
  34. PathNodePtr neighbor = NULL;
  35. PathNodePtr terminate_node = NULL;
  36. bool init_search = init;
  37. const int tolerance = ceil(1 / resolution_); // ceil()函数:求不小于x的最小整数(向上取整)

前面是一些初始化,定义了起点和终点状态,将起点加入open_set

下面进入主循环

  1. while (!open_set_.empty())
  2. {
  3. cur_node = open_set_.top(); // 取出cost最小的节点
  4. // Terminate? 判断扩展的节点是否距离终点过近
  5. bool reach_horizon = (cur_node->state.head(3) - start_pt).norm() >= horizon_;
  6. bool near_end = abs(cur_node->index(0) - end_index(0)) <= tolerance &&
  7. abs(cur_node->index(1) - end_index(1)) <= tolerance &&
  8. abs(cur_node->index(2) - end_index(2)) <= tolerance;
  9. if (reach_horizon || near_end)
  10. {
  11. terminate_node = cur_node;
  12. retrievePath(terminate_node);
  13. if (near_end)
  14. {
  15. // Check whether shot traj exist
  16. estimateHeuristic(cur_node->state, end_state, time_to_goal);
  17. computeShotTraj(cur_node->state, end_state, time_to_goal);
  18. if (init_search)
  19. ROS_ERROR("Shot in first search loop!");
  20. }
  21. }
  22. if (reach_horizon)
  23. {
  24. if (is_shot_succ_)
  25. {
  26. std::cout << "reach end" << std::endl;
  27. return REACH_END;
  28. }
  29. else
  30. {
  31. std::cout << "reach horizon" << std::endl;
  32. return REACH_HORIZON;
  33. }
  34. }
  35. if (near_end)
  36. {
  37. if (is_shot_succ_)
  38. {
  39. std::cout << "reach end" << std::endl;
  40. return REACH_END;
  41. }
  42. else if (cur_node->parent != NULL)
  43. {
  44. std::cout << "near end" << std::endl;
  45. return NEAR_END;
  46. }
  47. else
  48. {
  49. std::cout << "no path" << std::endl;
  50. return NO_PATH;
  51. }
  52. }

定义了一些边界条件,如果距离终点或者地图边界过近就返回

  1. // 弹出节点
  2. open_set_.pop();
  3. cur_node->node_state = IN_CLOSE_SET;
  4. iter_num_ += 1;
  5. double res = 1 / 2.0, time_res = 1 / 1.0, time_res_init = 1 / 20.0;
  6. Eigen::Matrix<double, 6, 1> cur_state = cur_node->state;
  7. Eigen::Matrix<double, 6, 1> pro_state;
  8. vector<PathNodePtr> tmp_expand_nodes;
  9. Eigen::Vector3d um;
  10. double pro_t;
  11. vector<Eigen::Vector3d> inputs;
  12. vector<double> durations;
  13. if (init_search)
  14. {
  15. inputs.push_back(start_acc_);
  16. for (double tau = time_res_init * init_max_tau_; tau <= init_max_tau_ + 1e-3;
  17. tau += time_res_init * init_max_tau_)
  18. durations.push_back(tau);
  19. init_search = false;
  20. }
  21. else
  22. {
  23. // inputs里面放入所有可能的三维加速度
  24. for (double ax = -max_acc_; ax <= max_acc_ + 1e-3; ax += max_acc_ * res)
  25. for (double ay = -max_acc_; ay <= max_acc_ + 1e-3; ay += max_acc_ * res)
  26. for (double az = -max_acc_; az <= max_acc_ + 1e-3; az += max_acc_ * res)
  27. {
  28. um << ax, ay, az;
  29. inputs.push_back(um);
  30. }
  31. // duration里面放所有可能的tau
  32. for (double tau = time_res * max_tau_; tau <= max_tau_; tau += time_res * max_tau_)
  33. durations.push_back(tau);
  34. }

接下来弹出cost最小的节点,然后对周围所有可能的输入加速度以及时间间隔进行遍历,获取数组inputs以及durations

  1. for (int i = 0; i < inputs.size(); ++i)
  2. for (int j = 0; j < durations.size(); ++j)
  3. {
  4. um = inputs[i];
  5. double tau = durations[j];
  6. stateTransit(cur_state, pro_state, um, tau);// 记录状态转移:从当前状态转移到pro_state,这个函数得到pro_state
  7. pro_t = cur_node->time + tau; // 时间间隔
  8. Eigen::Vector3d pro_pos = pro_state.head(3); // 取 pro_state 前3维,作为位置
  9. // Check if in close set
  10. Eigen::Vector3i pro_id = posToIndex(pro_pos); // 坐标转换成idx
  11. int pro_t_id = timeToIndex(pro_t); // 时间也转换一下
  12. // 哈希表搜索这个节点,如果是动态的,搜索加上时间
  13. PathNodePtr pro_node = dynamic ? expanded_nodes_.find(pro_id, pro_t_id) : expanded_nodes_.find(pro_id);
  14. // 如果没搜索到(==NULL),或者说当前节点在CLOSE LIST内了
  15. if (pro_node != NULL && pro_node->node_state == IN_CLOSE_SET)
  16. {
  17. if (init_search) // 如果是
  18. std::cout << "close" << std::endl;
  19. continue;
  20. }

然后对所有可能的加速度和时间间隔,使用stateTransit函数计算可能转移到的状态

  1. // 状态转移,从state0转移到state1
  2. void KinodynamicAstar::stateTransit(Eigen::Matrix<double, 6, 1>& state0, Eigen::Matrix<double, 6, 1>& state1,
  3. Eigen::Vector3d um, double tau)
  4. {
  5. for (int i = 0; i < 3; ++i)
  6. phi_(i, i + 3) = tau;
  7. Eigen::Matrix<double, 6, 1> integral; // 创建一个向量存增量
  8. integral.head(3) = 0.5 * pow(tau, 2) * um; //位置为δx = 0.5 * a * t^2
  9. integral.tail(3) = tau * um; // 速度为 δv = a * t
  10. state1 = phi_ * state0 + integral;
  11. }

(3)搜索完毕

搜索完毕后,如果结果为NO_PATH,再次搜索一遍,如果再次为NO_PATH就结束

  1. if (status == KinodynamicAstar::NO_PATH) {
  2. cout << "[kino replan]: kinodynamic search fail!" << endl;
  3. // retry searching with discontinuous initial state
  4. kino_path_finder_->reset();
  5. status = kino_path_finder_->search(start_pt, start_vel, start_acc, end_pt, end_vel, false);
  6. if (status == KinodynamicAstar::NO_PATH) {
  7. cout << "[kino replan]: Can't find path." << endl;
  8. return false;
  9. } else {
  10. cout << "[kino replan]: retry search success." << endl;
  11. }
  12. } else {
  13. cout << "[kino replan]: kinodynamic search success." << endl;
  14. }

获取KinoAstar规划的路径,并以0.01的时间间隔采样,存储在kino_path_内

  1. plan_data_.kino_path_ = kino_path_finder_->getKinoTraj(0.01);
  2. t_search = (ros::Time::now() - t1).toSec();

3、B样条

下面就到了后端利用B样条来优化轨迹

(1)采样getSamples

  1. // parameterize the path to bspline
  2. double ts = pp_.ctrl_pt_dist / pp_.max_vel_;
  3. vector<Eigen::Vector3d> point_set, start_end_derivatives;
  4. kino_path_finder_->getSamples(ts, point_set, start_end_derivatives);

首先以最大的控制点距离(参数设置为0.5)除以最大速度,获取间隔时间,然后进入getSamples函数,首先计算所有节点之间的持续时间,加和得到T_sum

  1. void KinodynamicAstar::getSamples(double& ts, vector<Eigen::Vector3d>& point_set,
  2. vector<Eigen::Vector3d>& start_end_derivatives)
  3. {
  4. /* ---------- path duration ---------- */
  5. double T_sum = 0.0;
  6. if (is_shot_succ_)
  7. T_sum += t_shot_;
  8. PathNodePtr node = path_nodes_.back();
  9. while (node->parent != NULL)
  10. {
  11. T_sum += node->duration;
  12. node = node->parent;
  13. }
  14. // cout << "duration:" << T_sum << endl;

如果成功到达目标点,那么还要把到达目标点的轨迹时间也加上

下面计算末速度和末加速度

  1. // Calculate boundary vel and acc
  2. Eigen::Vector3d end_vel, end_acc;
  3. double t;
  4. if (is_shot_succ_)
  5. {
  6. t = t_shot_;
  7. end_vel = end_vel_;
  8. for (int dim = 0; dim < 3; ++dim)
  9. {
  10. Vector4d coe = coef_shot_.row(dim);
  11. end_acc(dim) = 2 * coe(2) + 6 * coe(3) * t_shot_;
  12. }
  13. }
  14. else
  15. {
  16. t = path_nodes_.back()->duration;
  17. end_vel = node->state.tail(3);
  18. end_acc = path_nodes_.back()->input;
  19. }

接下来进行采样

  1. // Get point samples
  2. int seg_num = floor(T_sum / ts);
  3. seg_num = max(8, seg_num);
  4. ts = T_sum / double(seg_num);
  5. bool sample_shot_traj = is_shot_succ_;
  6. node = path_nodes_.back();
  7. for (double ti = T_sum; ti > -1e-5; ti -= ts)
  8. {
  9. if (sample_shot_traj)
  10. {
  11. // samples on shot traj
  12. Vector3d coord;
  13. Vector4d poly1d, time;
  14. for (int j = 0; j < 4; j++)
  15. time(j) = pow(t, j);
  16. for (int dim = 0; dim < 3; dim++)
  17. {
  18. poly1d = coef_shot_.row(dim);
  19. coord(dim) = poly1d.dot(time);
  20. }
  21. point_set.push_back(coord);
  22. t -= ts;
  23. /* end of segment */
  24. if (t < -1e-5)
  25. {
  26. sample_shot_traj = false;
  27. if (node->parent != NULL)
  28. t += node->duration;
  29. }
  30. }
  31. else
  32. {
  33. // samples on searched traj
  34. Eigen::Matrix<double, 6, 1> x0 = node->parent->state;
  35. Eigen::Matrix<double, 6, 1> xt;
  36. Vector3d ut = node->input;
  37. stateTransit(x0, xt, ut, t);
  38. point_set.push_back(xt.head(3));
  39. t -= ts;
  40. // cout << "t: " << t << ", t acc: " << T_accumulate << endl;
  41. if (t < -1e-5 && node->parent->parent != NULL)
  42. {
  43. node = node->parent;
  44. t += node->duration;
  45. }
  46. }
  47. }
  48. reverse(point_set.begin(), point_set.end());

分别对到达目标点的轨迹和中间的轨迹进行采样,间隔时间为ts,最后存在point_set内

ps:这里的ts重新计算,先根据T_sum和之前的ts算出中间插值样条线的数量seg_num,然后再算出每个seg的平均时间作为ts

最后计算起始加速度,然后获取start_end_derivatives

  1. // calculate start acc
  2. Eigen::Vector3d start_acc;
  3. if (path_nodes_.back()->parent == NULL)
  4. {
  5. // no searched traj, calculate by shot traj
  6. start_acc = 2 * coef_shot_.col(2);
  7. }
  8. else
  9. {
  10. // input of searched traj
  11. start_acc = node->input;
  12. }
  13. start_end_derivatives.push_back(start_vel_);
  14. start_end_derivatives.push_back(end_vel);
  15. start_end_derivatives.push_back(start_acc);
  16. start_end_derivatives.push_back(end_acc);

起始加速度,如果中间没有搜索的轨迹,使用shot轨迹来计算,否则就采用搜索轨迹的第一个点的input作为起始加速度。

start_end_derivatives是一个size为4的vector,存着起始和末速度加速度

(2)B样条控制点生成parameterizeToBspline

创建一个存储B样条控制点的矩阵ctrl_pts,然后运行parameterizeToBspline函数来获取控制点

  1. Eigen::MatrixXd ctrl_pts;
  2. NonUniformBspline::parameterizeToBspline(ts, point_set, start_end_derivatives, ctrl_pts);
  3. NonUniformBspline init(ctrl_pts, 3, ts);

下面是parameterizeToBspline的代码

  1. int K = point_set.size();
  2. // write A
  3. Eigen::Vector3d prow(3), vrow(3), arow(3);
  4. prow << 1, 4, 1;
  5. vrow << -1, 0, 1;
  6. arow << 1, -2, 1;
  7. Eigen::MatrixXd A = Eigen::MatrixXd::Zero(K + 4, K + 2);
  8. for (int i = 0; i < K; ++i) A.block(i, i, 1, 3) = (1 / 6.0) * prow.transpose();
  9. A.block(K, 0, 1, 3) = (1 / 2.0 / ts) * vrow.transpose();
  10. A.block(K + 1, K - 1, 1, 3) = (1 / 2.0 / ts) * vrow.transpose();
  11. A.block(K + 2, 0, 1, 3) = (1 / ts / ts) * arow.transpose();
  12. A.block(K + 3, K - 1, 1, 3) = (1 / ts / ts) * arow.transpose();

计算A矩阵

  1. // write b
  2. Eigen::VectorXd bx(K + 4), by(K + 4), bz(K + 4);
  3. for (int i = 0; i < K; ++i) {
  4. bx(i) = point_set[i](0);
  5. by(i) = point_set[i](1);
  6. bz(i) = point_set[i](2);
  7. }
  8. for (int i = 0; i < 4; ++i) {
  9. bx(K + i) = start_end_derivative[i](0);
  10. by(K + i) = start_end_derivative[i](1);
  11. bz(K + i) = start_end_derivative[i](2);
  12. }

计算b矩阵

求解方程Ax=b

  1. // solve Ax = b
  2. Eigen::VectorXd px = A.colPivHouseholderQr().solve(bx);
  3. Eigen::VectorXd py = A.colPivHouseholderQr().solve(by);
  4. Eigen::VectorXd pz = A.colPivHouseholderQr().solve(bz);

求出来解就是控制点的坐标

  1. // convert to control pts
  2. ctrl_pts.resize(K + 2, 3);
  3. ctrl_pts.col(0) = px;
  4. ctrl_pts.col(1) = py;
  5. ctrl_pts.col(2) = pz;

(3)B样条轨迹优化BsplineOptimizeTraj

执行函数BsplineOptimizeTraj来对控制点进行轨迹优化

  1. // bspline trajectory optimization
  2. t1 = ros::Time::now();
  3. int cost_function = BsplineOptimizer::NORMAL_PHASE;
  4. if (status != KinodynamicAstar::REACH_END) {
  5. cost_function |= BsplineOptimizer::ENDPOINT;
  6. }
  7. ctrl_pts = bspline_optimizers_[0]->BsplineOptimizeTraj(ctrl_pts, ts, cost_function, 1, 1);
  8. t_opt = (ros::Time::now() - t1).toSec();

设置 cost_function为BsplineOptimizer::NORMAL_PHASE;

  1. const int BsplineOptimizer::NORMAL_PHASE =
  2. BsplineOptimizer::SMOOTHNESS | BsplineOptimizer::DISTANCE | BsplineOptimizer::FEASIBILITY;

其中包含了三种优化,一种是平滑性,一种是距离,一种是可行性。

如果没有到达终点,再增加ENDPOINT优化。

接下来执行优化

  1. Eigen::MatrixXd BsplineOptimizer::BsplineOptimizeTraj(const Eigen::MatrixXd& points, const double& ts,
  2. const int& cost_function, int max_num_id,
  3. int max_time_id) {
  4. setControlPoints(points);
  5. setBsplineInterval(ts);
  6. setCostFunction(cost_function);
  7. setTerminateCond(max_num_id, max_time_id);
  8. optimize();
  9. return this->control_points_;
  10. }

首先设置控制点和时间间隔,维度为3维,间隔时间ts。然后设置CostFunction为目标函数。max_num_id和max_time_id都为1。

  1. void BsplineOptimizer::setControlPoints(const Eigen::MatrixXd& points) {
  2. control_points_ = points;
  3. dim_ = control_points_.cols();
  4. }
  5. void BsplineOptimizer::setBsplineInterval(const double& ts) { bspline_interval_ = ts; }
  6. void BsplineOptimizer::setCostFunction(const int& cost_code) {
  7. cost_function_ = cost_code;
  8. // print optimized cost function
  9. string cost_str;
  10. if (cost_function_ & SMOOTHNESS) cost_str += "smooth |";
  11. if (cost_function_ & DISTANCE) cost_str += " dist |";
  12. if (cost_function_ & FEASIBILITY) cost_str += " feasi |";
  13. if (cost_function_ & ENDPOINT) cost_str += " endpt |";
  14. if (cost_function_ & GUIDE) cost_str += " guide |";
  15. if (cost_function_ & WAYPOINTS) cost_str += " waypt |";
  16. ROS_INFO_STREAM("cost func: " << cost_str);
  17. }
  18. void BsplineOptimizer::setTerminateCond(const int& max_num_id, const int& max_time_id) {
  19. max_num_id_ = max_num_id;
  20. max_time_id_ = max_time_id;
  21. }

然后执行优化,返回优化后的控制点坐标。

(4)控制点时间间隔调整reallocateTime

优化完控制点坐标,需要进行时间间隔调整

  1. // iterative time adjustment
  2. t1 = ros::Time::now();
  3. NonUniformBspline pos = NonUniformBspline(ctrl_pts, 3, ts);
  4. double to = pos.getTimeSum();
  5. pos.setPhysicalLimits(pp_.max_vel_, pp_.max_acc_);
  6. bool feasible = pos.checkFeasibility(false);
  7. int iter_num = 0;
  8. while (!feasible && ros::ok()) {
  9. feasible = pos.reallocateTime();
  10. if (++iter_num >= 3) break;
  11. }
  12. // pos.checkFeasibility(true);
  13. // cout << "[Main]: iter num: " << iter_num << endl;
  14. double tn = pos.getTimeSum();
  15. cout << "[kino replan]: Reallocate ratio: " << tn / to << endl;
  16. if (tn / to > 3.0) ROS_ERROR("reallocate error.");
  17. t_adjust = (ros::Time::now() - t1).toSec();

创建一个B样条pos来存储优化后的B样条,进行时间重分配

我们先来看一下这个B样条怎么创建的

  1. NonUniformBspline::NonUniformBspline(const Eigen::MatrixXd& points, const int& order,
  2. const double& interval) {
  3. setUniformBspline(points, order, interval);
  4. }
  5. void NonUniformBspline::setUniformBspline(const Eigen::MatrixXd& points, const int& order,
  6. const double& interval) {
  7. control_points_ = points;
  8. p_ = order;
  9. interval_ = interval;
  10. n_ = points.rows() - 1;
  11. m_ = n_ + p_ + 1;
  12. u_ = Eigen::VectorXd::Zero(m_ + 1);
  13. for (int i = 0; i <= m_; ++i) {
  14. if (i <= p_) {
  15. u_(i) = double(-p_ + i) * interval_;
  16. } else if (i > p_ && i <= m_ - p_) {
  17. u_(i) = u_(i - 1) + interval_;
  18. } else if (i > m_ - p_) {
  19. u_(i) = u_(i - 1) + interval_;
  20. }
  21. }
  22. }

根据构造函数,可以知道它把维度赋给了p_,然后时间间隔为interval_,

然后使用n_存储控制点的数量,m_存储控制点数量+维度+1,u_来存储每个控制点之间的时间间隔

to来存储控制点之间的时间总和

  1. double NonUniformBspline::getTimeSum() {
  2. double tm, tmp;
  3. getTimeSpan(tm, tmp);
  4. return tmp - tm;
  5. }
  6. void NonUniformBspline::getTimeSpan(double& um, double& um_p) {
  7. um = u_(p_);
  8. um_p = u_(m_ - p_);
  9. }

为pos设置最大的限制   pos.setPhysicalLimits(pp_.max_vel_, pp_.max_acc_);

然后检测pos的可行性

最后进行三次重分配时间的计算

  1. bool NonUniformBspline::reallocateTime(bool show) {
  2. // SETY << "[Bspline]: total points size: " << control_points_.rows() << endl;
  3. // cout << "origin knots:\n" << u_.transpose() << endl;
  4. bool fea = true;
  5. Eigen::MatrixXd P = control_points_;
  6. int dimension = control_points_.cols();
  7. double max_vel, max_acc;

使用P存储控制点,dimension为3

检测速度可行性,然后插入中间点

  1. /* check vel feasibility and insert points */
  2. for (int i = 0; i < P.rows() - 1; ++i) {
  3. Eigen::VectorXd vel = p_ * (P.row(i + 1) - P.row(i)) / (u_(i + p_ + 1) - u_(i + 1));
  4. if (fabs(vel(0)) > limit_vel_ + 1e-4 || fabs(vel(1)) > limit_vel_ + 1e-4 ||
  5. fabs(vel(2)) > limit_vel_ + 1e-4) {
  6. fea = false;
  7. if (show) cout << "[Realloc]: Infeasible vel " << i << " :" << vel.transpose() << endl;
  8. max_vel = -1.0;
  9. for (int j = 0; j < dimension; ++j) {
  10. max_vel = max(max_vel, fabs(vel(j)));
  11. }
  12. double ratio = max_vel / limit_vel_ + 1e-4;
  13. if (ratio > limit_ratio_) ratio = limit_ratio_;
  14. double time_ori = u_(i + p_ + 1) - u_(i + 1);
  15. double time_new = ratio * time_ori;
  16. double delta_t = time_new - time_ori;
  17. double t_inc = delta_t / double(p_);
  18. for (int j = i + 2; j <= i + p_ + 1; ++j) {
  19. u_(j) += double(j - i - 1) * t_inc;
  20. if (j <= 5 && j >= 1) {
  21. // cout << "vel j: " << j << endl;
  22. }
  23. }
  24. for (int j = i + p_ + 2; j < u_.rows(); ++j) {
  25. u_(j) += delta_t;
  26. }
  27. }
  28. }

遍历每个控制点,计算两个控制点之间的速度 v = x/t。如果速度没有越limit_vel_界,则以这个速度作为重分配的时间的速度。

然后遍历每个维度,找到三个维度上最大的速度max_vel,计算max_vel和limit_vel_的比值ratio,如果这个重分配的比值ratio大于limit_ratio_,就让它等于limit_ratio_。

接下来计算原来的间隔时间time_ori,给它乘以比率得到time_new,计算差值delta_t,

后面加速度同理

  1. /* acc feasibility */
  2. for (int i = 0; i < P.rows() - 2; ++i) {
  3. Eigen::VectorXd acc = p_ * (p_ - 1) *
  4. ((P.row(i + 2) - P.row(i + 1)) / (u_(i + p_ + 2) - u_(i + 2)) -
  5. (P.row(i + 1) - P.row(i)) / (u_(i + p_ + 1) - u_(i + 1))) /
  6. (u_(i + p_ + 1) - u_(i + 2));
  7. if (fabs(acc(0)) > limit_acc_ + 1e-4 || fabs(acc(1)) > limit_acc_ + 1e-4 ||
  8. fabs(acc(2)) > limit_acc_ + 1e-4) {
  9. fea = false;
  10. if (show) cout << "[Realloc]: Infeasible acc " << i << " :" << acc.transpose() << endl;
  11. max_acc = -1.0;
  12. for (int j = 0; j < dimension; ++j) {
  13. max_acc = max(max_acc, fabs(acc(j)));
  14. }
  15. double ratio = sqrt(max_acc / limit_acc_) + 1e-4;
  16. if (ratio > limit_ratio_) ratio = limit_ratio_;
  17. // cout << "ratio: " << ratio << endl;
  18. double time_ori = u_(i + p_ + 1) - u_(i + 2);
  19. double time_new = ratio * time_ori;
  20. double delta_t = time_new - time_ori;
  21. double t_inc = delta_t / double(p_ - 1);
  22. if (i == 1 || i == 2) {
  23. // cout << "acc i: " << i << endl;
  24. for (int j = 2; j <= 5; ++j) {
  25. u_(j) += double(j - 1) * t_inc;
  26. }
  27. for (int j = 6; j < u_.rows(); ++j) {
  28. u_(j) += 4.0 * t_inc;
  29. }
  30. } else {
  31. for (int j = i + 3; j <= i + p_ + 1; ++j) {
  32. u_(j) += double(j - i - 2) * t_inc;
  33. if (j <= 5 && j >= 1) {
  34. // cout << "acc j: " << j << endl;
  35. }
  36. }
  37. for (int j = i + p_ + 2; j < u_.rows(); ++j) {
  38. u_(j) += delta_t;
  39. }
  40. }
  41. }
  42. }
  43. return fea;

(5)更新轨迹updateTrajInfo

调整完时间间隔,再把新的轨迹更新上去

  1. double tn = pos.getTimeSum();
  2. cout << "[kino replan]: Reallocate ratio: " << tn / to << endl;
  3. if (tn / to > 3.0) ROS_ERROR("reallocate error.");
  4. t_adjust = (ros::Time::now() - t1).toSec();
  5. // save planned results
  6. local_data_.position_traj_ = pos;
  7. double t_total = t_search + t_opt + t_adjust;
  8. cout << "[kino replan]: time: " << t_total << ", search: " << t_search << ", optimize: " << t_opt
  9. << ", adjust time:" << t_adjust << endl;
  10. pp_.time_search_ = t_search;
  11. pp_.time_optimize_ = t_opt;
  12. pp_.time_adjust_ = t_adjust;
  13. updateTrajInfo();
  14. return true;

首先获取新的时间总和tn,然后将pos作为新的轨迹存储在position_traj_

接下来更新轨迹的信息

  1. void FastPlannerManager::updateTrajInfo() {
  2. local_data_.velocity_traj_ = local_data_.position_traj_.getDerivative();
  3. local_data_.acceleration_traj_ = local_data_.velocity_traj_.getDerivative();
  4. local_data_.start_pos_ = local_data_.position_traj_.evaluateDeBoorT(0.0);
  5. local_data_.duration_ = local_data_.position_traj_.getTimeSum();
  6. local_data_.traj_id_ += 1;
  7. }

获取速度轨迹,加速度轨迹,起点坐标,持续时间,然后给轨迹id+1

已知位置的轨迹,获取速度的轨迹

  1. NonUniformBspline NonUniformBspline::getDerivative() {
  2. Eigen::MatrixXd ctp = getDerivativeControlPoints();
  3. NonUniformBspline derivative(ctp, p_ - 1, interval_);
  4. /* cut the first and last knot */
  5. Eigen::VectorXd knot(u_.rows() - 2);
  6. knot = u_.segment(1, u_.rows() - 2);
  7. derivative.setKnot(knot);
  8. return derivative;
  9. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/542854?site
推荐阅读
相关标签
  

闽ICP备14008679号