赞
踩
(代码注释中英夹杂,英文注释是原来作者的注释,中文注释是本人后来添加的)
1.进入到工作空间下运行catkin_make编译;
2.运行source devel/setup.bash;
3.打开一个新的终端运行roscore;
4.回到原终端窗口运行roslaunch waypoint_trajectory_generator test.launch;
5.rviz运行起来之后,按如下步骤操作:
结束的操作
最终的效果
#ifndef _TRAJECTORY_GENERATOR_WAYPOINT_H_ #define _TRAJECTORY_GENERATOR_WAYPOINT_H_ //#include <Eigen/Eigen> #include <eigen3/Eigen/Eigen> #include <vector> class TrajectoryGeneratorWaypoint { private: // 原作者给出的,闭式求解没有用到 double _qp_cost{}; Eigen::MatrixXd _Q; Eigen::VectorXd _Px, _Py, _Pz; public: TrajectoryGeneratorWaypoint(); ~TrajectoryGeneratorWaypoint(); // 生成多项式系数 Eigen::MatrixXd PolyQPGeneration( int order, const Eigen::MatrixXd &Path, const Eigen::MatrixXd &Vel, const Eigen::MatrixXd &Acc, const Eigen::VectorXd &Time); // Factorial阶乘 static int Factorial(int x); // 获得Ct矩阵 Eigen::MatrixXd getCt(int n_seg, int d_order); // 获得M矩阵 Eigen::MatrixXd getM(int n_seg, int d_order, int p_num1d, const Eigen::VectorXd &ts); // 获得Q矩阵 Eigen::MatrixXd getQ(int n_seg, int d_order, int p_num1d, const Eigen::VectorXd &ts); }; #endif
实现阶乘;
int TrajectoryGeneratorWaypoint::Factorial(int x)
{
int fac = 1;
for(int i = x; i > 0; i--)
fac = fac * i;
return fac;
}
下面的代码是原作者给出的,我贴到这里,但是我并没有理解作者的思路,所以后面又自己写了两种获得Ct矩阵的方法便于后来者理解,对于Ct矩阵的理论部分可参考上一篇博文《Minimun Snap轨迹优化学习记录》中第三小节<3.Minimun Snap闭式求解(Closed-form Solution for Minimun Snap)>的第四张图片;
MatrixXd TrajectoryGeneratorWaypoint::getCt(int const n_seg, int const d_order){ // d_order is 4 /* * get Selection Matrix * args: * n_seg: the number of segments * d_order: the deferential order: if minimal jerk. it is 3, * return: * Ct: a matrix, * row corresponds to original variable vector d ( 2 * d_order * n_seg ) * column corresponds to [ dF, dP ]' ( d_order*2*n_seg - (n_seg-1)*d_order ) * Note: the variables implicitly eliminates same variables"" */ int ct_rows = d_order*2*n_seg; int ct_cols = d_order*2*n_seg - (n_seg-1)*d_order; MatrixXd Ct = MatrixXd::Zero(ct_rows, ct_cols); vector<int> d_vector; for(int k = 0; k < n_seg; k ++){ // for(int t = 0; t < 2; t++){ for(int d = 0; d < d_order; d++){ d_vector.push_back(k*100+t*10+d); } } } // cout << "---------------" << endl; // cout << "d_vector:" << endl; // for(vector<int>::iterator it = d_vector.begin(); it != d_vector.end(); it++) // cout << *it << endl; // cout << "---------------" << endl; int val, row; int col = 0; // column of one in Ct // fixed starting point at segment 0 in [ dF, dP ]' int k = 0; int t = 0; int d = 0; for(d = 0; d < d_order; d++){ val = k * 100 + t * 10 + d; auto it = std::find(d_vector.begin(), d_vector.end(), val); // std::distance,返回两个迭代器之间的距离 row = std::distance(d_vector.begin(), it); Ct(row, col) = 1; col += 1; } // fixed final point at segment 0, 1, 2, n_seg-2 in [ dF, dP ]' t = 1; d = 0; for(k = 0; k < n_seg - 1; k++){ val = k * 100 + t * 10 + d; auto it = std::find(d_vector.begin(), d_vector.end(), val); row = std::distance(d_vector.begin(), it); Ct(row, col) = 1; val = (k + 1) * 100 + (t - 1) * 10 + d; it= std::find(d_vector.begin(), d_vector.end(), val); row = std::distance(d_vector.begin(), it); Ct(row, col) = 1; col += 1; } // fixed final point at segment n_seg-1 in [ dF, dP ]' k = n_seg - 1; t = 1; d = 0; for(d = 0; d < d_order; d++){ val = k * 100 + t * 10 + d; auto it = std::find(d_vector.begin(), d_vector.end(), val); row = std::distance(d_vector.begin(), it); Ct(row, col) = 1; col += 1; } // free variables at segment 0, 1, 2, n_seg-1 in [ dF, dP ]' k = 0; t = 1; d = 1; for(k = 0; k < n_seg - 1; k++){ for(d = 1; d < d_order; d++){ val = k * 100 + t * 10 + d; auto it = std::find(d_vector.begin(), d_vector.end(), val); row = std::distance(d_vector.begin(), it); Ct(row, col) = 1; val = (k + 1) * 100 + (t - 1) * 10 + d; it = std::find(d_vector.begin(), d_vector.end(), val); row = std::distance(d_vector.begin(), it); Ct(row, col) = 1; col += 1; } } return Ct; }
比较容易理解的方法一:
MatrixXd TrajectoryGeneratorWaypoint::getCt(int const n_seg, int const d_order){ // d_order is 4 /* * get Selection Matrix * args: * n_seg: the number of segments * d_order: the deferential order: if minimal jerk. it is 3, * return: * Ct: a matrix, * row corresponds to original variable vector d ( 2 * d_order * n_seg ) * column corresponds to [ dF, dP ]' ( d_order*2*n_seg - (n_seg-1)*d_order ) * Note: the variables implicitly eliminates same variables"" */ /* * 阅读代码之前,请仔细理解 d = Ct*[dF dP]t,以及[dF dP]和矩阵d的内容. * 对于 d_order=4 n_seg=3 的情况: * [dF dP]t = [p0 v0 a0 j0 p1 p2 p3 v3 a3 j3, v1 a1 j1 v2 a2 j2] * dt = [p0 v0 a0 j0 p1 v1 a1 j1 p1 v1 a1 j1 p2 v2 a2 j2 p2 v2 a2 j2 p3 v3 a3 j3] */ // 确定Ct矩阵尺寸,可参考https://blog.csdn.net/weixin_44558122/article/details/116173197#t3中<<3.Minimun Snap闭式求 // 解(Closed-form Solution for Minimun Snap)>>部分第四张图片的例子 int ct_rows = d_order*2*n_seg; int ct_cols = d_order*2*n_seg - (n_seg-1)*d_order; MatrixXd Ct = MatrixXd::Zero(ct_rows, ct_cols); string p("p"); string v("v"); string a("a"); string j("j"); // 构造dfdp vector<string> dfdp; for(int k = 0; k < n_seg+1; k++){ if(k == 0 || k == n_seg){ dfdp.push_back(p+to_string(k)); dfdp.push_back(v+to_string(k)); dfdp.push_back(a+to_string(k)); dfdp.push_back(j+to_string(k)); } else { dfdp.push_back(p+to_string(k)); } } for(int i = 1; i < n_seg; i++){ dfdp.push_back(v+to_string(i)); dfdp.push_back(a+to_string(i)); dfdp.push_back(j+to_string(i)); } // for(auto it = dfdp.begin(); it != dfdp.end(); it++) // cout << *it << " "; // cout << endl; // 构造d vector<string> d; for(int k = 0; k < n_seg+1; k++){ if(k == 0 || k == n_seg){ d.push_back(p+to_string(k)); d.push_back(v+to_string(k)); d.push_back(a+to_string(k)); d.push_back(j+to_string(k)); } else { d.push_back(p+to_string(k)); d.push_back(v+to_string(k)); d.push_back(a+to_string(k)); d.push_back(j+to_string(k)); d.push_back(p+to_string(k)); d.push_back(v+to_string(k)); d.push_back(a+to_string(k)); d.push_back(j+to_string(k)); } } // for(auto it = d.begin(); it != d.end(); it++) // cout << *it << " "; // 通过查找约束(如p0、a1)在d和dfdp中的位置,对应Ct矩阵的行和列,对对应的位置赋1 for(auto it = d.begin(); it != d.end(); it++){ int row = distance(d.begin(),it); // 在dfdp中取找当前it迭代对象对应的d中的值 auto it1 = std::find(dfdp.begin(), dfdp.end(), *it); int col = distance(dfdp.begin(),it1); Ct(row,col) = 1; // cout << row << " " << col << endl; } return Ct; }
稍微有些绕的方法二:
MatrixXd TrajectoryGeneratorWaypoint::getCt(int const n_seg, int const d_order){ // d_order is 4 /* * get Selection Matrix * args: * n_seg: the number of segments * d_order: the deferential order: if minimal jerk. it is 3, * return: * Ct: a matrix, * row corresponds to original variable vector d ( 2 * d_order * n_seg ) * column corresponds to [ dF, dP ]' ( d_order*2*n_seg - (n_seg-1)*d_order ) * Note: the variables implicitly eliminates same variables"" */ // 阅读代码之前,请仔细理解 d = Ct*[dF dP]t,以及[dF dP]和矩阵d的内容. // 对于 d_order=4 n_seg=3 的情况: // [dF dP]t = [p0 v0 a0 j0 p1 p2 p3 v3 a3 j3, v1 a1 j1 v2 a2 j2] // dt = [p0 v0 a0 j0 p1 v1 a1 j1 p1 v1 a1 j1 p2 v2 a2 j2 p2 v2 a2 j2 p3 v3 a3 j3] int ct_rows = d_order*2*n_seg; int ct_cols = d_order*2*n_seg - (n_seg-1)*d_order; MatrixXd Ct = MatrixXd::Zero(ct_rows, ct_cols); // p0,v0,a0,j0 for(int i = 0; i < d_order; i++) Ct(i,i) = 1; // middle point p1,p2,...p(n-1) for(int i = 1; i < n_seg; i++){ // d_order+(i-1)*8-1+1: // d_order,起点约束个数; // i-1,表示这个中间点之前还有几个中间点; // *8,表示每个中间点的p,v,a,j都有两套; // -1,是因为在c++中,矩阵的下标表示是从0开始的,我们在下面推倒的时候用到矩阵下标都是从1开始; // +1,表示当前操作的是p,举个例子,比如对p1操作的时候,前面有4个约束,p0,v0,a0,j0,即d_order数量 // 的约束个数,+1就当前表示对p1操作,也就是第[dF dP]中的第五个; // d_order+(i-1)*1: (注:这里写成这样没做化简只是为了方便理解过程和思路) // d_order,起点约束个数; // i-1,表示这个中间点之前还有几个中间点; // *1表示每个中间点的p; Ct(d_order+(i-1)*8-1+1,d_order+(i-1)*1) = 1; Ct(d_order+(i-1)*8+4-1+1,d_order+(i-1)*1) = 1; } // pn,vn,an,jn for(int i = 0; i < d_order; i++) Ct(d_order+2*d_order*(n_seg-1)+i,d_order+n_seg-1+i) = 1; // v1,a1,j1,...,v(n-1),a(n-1),j(n-1) // k表示第几个中间点 for(int k = 1; k < n_seg; k++){ // i表示中间点需要求的dP,d_order=4时表示v,a,j,而p是需要给定的,属于dF; for(int i = 1; i < d_order; i++){ // ((k-1)*2+1)*d_order+1-1+i: (注:这里写成这样没做化简只是为了方便理解过程和思路) // k-1表示这个中间点之前还有几个中间点,比如k=1,表示第一个中间点,前面就是起点,没有中间点了,即k-1=0; // (k-1)*2,*2表示每个中间点的p,v,a,j都有两套(上一段轨迹的终点一套,下一段轨迹的起点一套); // ((k-1)*2+1)*d_order,*d_order表示每个点都有d_order个约束,+1表示起点的约束; // ((k-1)*2+1)*d_order+1,+1代表约束p,因为我们现在要处理的是约束vaj,p是给定的不需要在这里处理; // ((k-1)*2+1)*d_order+1-1,-1是因为在c++中,矩阵的下标表示是从0开始的,我们在下面推倒的时候用到矩阵下标都是从1开始; // ((k-1)*2+1)*d_order+1-1+i,+i表示当前是对vaj的哪一个约束操作 // (2*d_order+n_seg-1)-1+(k-1)*3+i): // 2*d_order,表示起点和终点; // n_seg-1,表示中间点的个数,中间点有几个,就有几个约束p; // (2*d_order+n_seg-1)-1,-1是因为在c++中,矩阵的下标表示是从0开始的,我们在下面推倒的时候用到矩阵下标都是从1开始; // (k-1)*3,k-1表示这个中间点之前还有几个中间点,*3表示每个点的vaj约束; // ((k-1)*2+1)*d_order+1-1+4+i: +4表示现在处理的是中间点两套pvaj约束的后面一套. Ct(((k-1)*2+1)*d_order+1-1+i,(2*d_order+n_seg-1)-1+(k-1)*3+i) = 1; Ct(((k-1)*2+1)*d_order+1-1+4+i,(2*d_order+n_seg-1)-1+(k-1)*3+i) = 1; } } return Ct; }
对于M矩阵的理论部分可参考上一篇博文《Minimun Snap轨迹优化学习记录》中第三小节<3.Minimun Snap闭式求解(Closed-form Solution for Minimun Snap)>的第一张图片,请结合后面的图片理解,代码如下:
MatrixXd TrajectoryGeneratorWaypoint::getM(int const n_seg, int const d_order, int const p_num1d, const Eigen::VectorXd &ts){ /* * get Mapping Matrix * args: * n_seg: the number of segments * d_order: the deferential order: if minimal jerk. it is 3, * p_num1d: the number of variables in each segment, if minimal jerk, it is 6 * ts: time allocation as a column vector, size: n_seg x 1, * return: * M: a matrix, size: n_seg * p_num1d x n_seg * p_num1d */ // MatrixXd M, M_temp, zeros; MatrixXd M = MatrixXd::Zero(n_seg * p_num1d, n_seg * p_num1d); MatrixXd coeff(d_order, p_num1d); if(d_order == 4){ coeff << 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 2, 3, 4, 5, 6, 7, 0, 0, 2, 6, 12, 20, 30, 42, 0, 0, 0, 6, 24, 60, 120,210; } else if(d_order == 3){ coeff << 1, 1, 1, 1, 1, 1, 0, 1, 2, 3, 4, 5, 0, 0, 2, 6, 12, 20; } else{ cout << "This derivatieve order is not provided getM!!!" << endl; } double t; for(int k = 0; k < n_seg; k++){ // calculate M_k of the k-th segment MatrixXd M_k = MatrixXd::Zero(p_num1d, p_num1d); // Matrix size: p_num1d x p_num1d // time of the k-th segment t = ts(k); for(int i = 0; i < d_order; i++){ M_k(i, i) = coeff(i, i); } for(int i = 0; i < d_order; i++){ for(int j = i; j < p_num1d; j++){ if( i == j){ M_k(i+d_order, j) = coeff(i, j) ; } else{ M_k(i+d_order, j) = coeff(i, j) * pow(t, j - i); } } } // cout << "---------------" << endl; // cout << "M_old:" << endl; // cout << M << endl; M.block(k*p_num1d, k*p_num1d, p_num1d, p_num1d) = M_k; // cout << "---------------" << endl; // cout << "M_new:" << endl; // cout << M << endl; } return M; }
循环之前原始M矩阵:
第一次循环后添加的部分:
上面M_new图中上面的红色框部分的解释:
上面M_new图中下面的红色框部分的解释:
选取M_new图中的箭头指向的315.182作为例子计算一下:
最终的到的M矩阵
对于Q矩阵的理论部分可参考上一篇博文《Minimun Snap轨迹优化学习记录》中第三小节<2.Minimun Snap轨迹生成(Minimun Snap Optimization))>的第二张和第三张图片,请结合后面的图片理解,代码如下:
Eigen::MatrixXd TrajectoryGeneratorWaypoint::getQ(int const n_seg, int const d_order, int const p_num1d, const Eigen::VectorXd &ts){ /* * get Q matrix * args: * n_seg: the number of segments * d_order: the deferential order: if minimal snap. it is 4 * p_num1d: the number of variables in each segment * ts: time allocation as a column vector, size: n_seg x 1, * return: * Q: a matrix, size: n_seg * p_num1d x n_seg * p_num1d * Note: p = [p0, p1, p2,...pn-1]' */ MatrixXd Q = MatrixXd::Zero(n_seg * p_num1d, n_seg * p_num1d); for(int k = 0; k < n_seg; k++) { Eigen::MatrixXd Q_k = Eigen::MatrixXd::Zero(p_num1d, p_num1d); // Matrix size: p_num1d x p_num1d for (int i = 0; i < p_num1d; i++) { for (int j = 0; j < p_num1d; j++) { // 只有当i和j都同时大于等于4的时候(i,j是从0开始的,正常我们表示矩阵的时候下标是从1开始算的,要注意这点),才会到else里执行计算 if (i < d_order || j < d_order) continue; else // Factorial,表示阶乘;下面公式参考:https://blog.csdn.net/weixin_44558122/article/details/116173197#t3中<<2.Minimun Snap轨迹 // 生成(Minimun Snap Optimization)>>部分第三张图片,对于pow(ts(k), i + j - 2 * d_order + 1),这里为什么没有 (t下标i-1)^(r+c-7) // 这一项了?是因为我们采用的时间分配方法是相对时间,每一段轨迹的开头都是0. Q_k(i, j) = Factorial(i) / Factorial(i - d_order) * Factorial(j) / Factorial(j - d_order) * 1 / (i + j - d_order * 2 + 1) * pow(ts(k), i + j - 2 * d_order + 1); } } // 矩阵块操作,可参考:https://gaoyichao.com/Xiaotu/?book=eigen&title=chapter4 // cout << "---------------" << endl; // cout << "Q_old:" << endl; // cout << Q << endl; Q.block(k * p_num1d, k * p_num1d, p_num1d, p_num1d) = Q_k; // cout << "Q_new:" << endl; // cout << Q << endl; // cout << "---------------" << endl; } return Q; }
对于Q1的求解:
最终得到的Q矩阵
作用:求解多项式系数;
Eigen::MatrixXd TrajectoryGeneratorWaypoint::PolyQPGeneration( const int d_order, // the order of derivative const Eigen::MatrixXd &Path, // waypoints coordinates (3d) const Eigen::MatrixXd &Vel, // boundary velocity const Eigen::MatrixXd &Acc, // boundary acceleration const Eigen::VectorXd &Time) // time allocation in each segment { // enforce initial and final velocity and acceleration, for higher order derivatives, just assume them be 0; int p_order = 2 * d_order - 1; // the order of polynomial int p_num1d = p_order + 1; // the number of variables in each segment int n_segment = Time.size(); // the number of segments // 3 * p_num1d, 三维坐标x,y,z, 分别对每一个维度去求解 MatrixXd PolyCoeff = MatrixXd::Zero(n_segment, 3 * p_num1d); // position(x,y,z), so we need (3 * p_num1d) coefficients // 这部分后面没有用到 VectorXd Px(p_num1d * n_segment), Py(p_num1d * n_segment), Pz(p_num1d * n_segment); // get Q MatrixXd Q = getQ(n_segment, d_order, p_num1d, Time); cout << "Matrix Q is:\n"<< endl << Q << endl; /* Produce Mapping Matrix to the entire trajectory, * M is a mapping matrix that maps polynomial coefficients to derivatives. */ MatrixXd M = getM(n_segment, d_order, p_num1d, Time); cout << "Mapping matrix M is:\n" << endl << M << endl; // compute Ct MatrixXd Ct = getCt(n_segment, d_order); cout <<"Ct: \n"<< endl << Ct << endl; MatrixXd C = Ct.transpose(); cout << "C is:\n" << endl << C << endl; MatrixXd M_inv = M.inverse(); MatrixXd M_inv_t = M_inv.transpose(); cout << "M_inv is:" << endl << M_inv << endl; cout << "M_inv_transp is:" << endl << M_inv_t << endl; cout << "size of C is: " << C.rows() << ", " << C.cols() << endl; cout << "size of M_inv_t is: " << M_inv_t.rows() << ", " << M_inv_t.cols() << endl; cout << "size of Q is: " << Q.rows() << ", " << Q.cols() << endl; cout << "size of M_inv is: " << M_inv.cols() << ", " << M_inv.rows() << endl; cout << "size of Ct is: " << Ct.rows() << ", " << Ct.cols() << endl; MatrixXd R = C * M_inv_t * Q * M_inv * Ct; // M is not changed cout << "R is:\n" << endl << R << endl; int num_d_F = 2 * d_order + n_segment - 1; int num_d_P = (n_segment - 1) * (d_order - 1); // 矩阵的角相关操作:https://blog.csdn.net/qq_33160678/article/details/112567335 MatrixXd R_pp = R.bottomRightCorner(num_d_P, num_d_P); // 6*6 cout << "R_pp is:\n" << endl << R_pp << endl; MatrixXd R_fp = R.topRightCorner(num_d_F, num_d_P); // 10*6 cout << "R_fp is:\n" << endl << R_fp << endl; // STEP 3: compute dF for x, y, z respectively for(int dim = 0; dim < 3; dim++){ VectorXd wayPoints = Path.col(dim); cout << "waypoints: " << endl << wayPoints << endl; VectorXd d_F = VectorXd::Zero(num_d_F); d_F(0) = wayPoints(0); //p0 // v0,0 a0,0 ... are 0 // pT,0, pT,1, ,,PT,n_seg-2 for(int i = 0; i < n_segment - 1; i++ ){ d_F(d_order + i) = wayPoints(i + 1); } // pT,n_seg-1 d_F(d_order + n_segment - 1) = wayPoints(n_segment); cout << "d_F is:" << endl << d_F << endl; VectorXd d_P = -1.0 * R_pp.inverse() * R_fp.transpose() * d_F; cout << "d_P is:" << endl << d_P << endl; VectorXd d_total(d_F.rows() + d_P.rows()); d_total << d_F, d_P; cout << "d_total is:" << endl << d_total << endl; // M.inverse() * Ct * d_total也即QP问题中的P矩阵,P矩阵就是x(t)多项式中各项的系数 VectorXd poly_coef_1d = M.inverse() * Ct * d_total; cout<< "Dimension " << dim <<" coefficients: "<< endl << poly_coef_1d << endl; MatrixXd poly_coef_1d_t = poly_coef_1d.transpose(); cout << "poly_coef_1d.transpose() :" << endl << poly_coef_1d_t << endl; // PolyCoeff(n_segment, 3 * p_num1d) for(int k = 0; k < n_segment; k++){ PolyCoeff.block(k, dim*p_num1d, 1, p_num1d) = poly_coef_1d_t.block(0,k*p_num1d, 1, p_num1d); } } cout << "PolyCoeff :" << endl << PolyCoeff << endl; return PolyCoeff; }
作用:获得输入的所有点坐标,即演示过程中的第五部分;
void rcvWaypointsCallBack(const nav_msgs::Path & wp) { vector<Vector3d> wp_list; wp_list.clear(); for (int k = 0; k < (int)wp.poses.size(); k++) { // 记录所有路标点 Vector3d pt( wp.poses[k].pose.position.x, wp.poses[k].pose.position.y, wp.poses[k].pose.position.z); wp_list.push_back(pt); // 如果路标点的Z小于0,退出循环 if(wp.poses[k].pose.position.z < 0.0) break; } // MatrixXd:表示任意大小的元素类型为double的矩阵变量,其大小只有在运行时被赋值之后才能知道。 MatrixXd waypoints(wp_list.size() + 1, 3); // 将所有路标点的坐标放到waypoints矩阵里 waypoints.row(0) = _startPos; for(int k = 0; k < (int)wp_list.size(); k++) waypoints.row(k+1) = wp_list[k]; //Trajectory generation: use minimum snap trajectory generation method //waypoints is the result of path planning (Manual in this homework) trajGeneration(waypoints); }
作用:轨迹生成;
void trajGeneration(Eigen::MatrixXd path) { TrajectoryGeneratorWaypoint trajectoryGeneratorWaypoint; MatrixXd vel = MatrixXd::Zero(2, 3); MatrixXd acc = MatrixXd::Zero(2, 3); vel.row(0) = _startVel; // give an arbitraty time allocation, all set all durations as 1 in the commented function. _polyTime = timeAllocation(path); // generate a minimum-snap piecewise monomial polynomial-based trajectory _polyCoeff = trajectoryGeneratorWaypoint.PolyQPGeneration(_dev_order, path, vel, acc, _polyTime); // 可视化path,即将所有点用直线连接起来 visWayPointPath(path); //After you finish your homework, you can use the function visWayPointTraj below to visulize your trajectory // 可视化优化过的轨迹 visWayPointTraj( _polyCoeff, _polyTime); }
作用:可视化优化过的轨迹;
void visWayPointTraj( MatrixXd polyCoeff, VectorXd time) { // visualization_msgs相关参考:https://blog.csdn.net/u013834525/article/details/80447931 // 或者直接参考官网:http://wiki.ros.org/rviz/DisplayTypes/Marker#The_Marker_Message visualization_msgs::Marker _traj_vis; _traj_vis.header.stamp = ros::Time::now(); _traj_vis.header.frame_id = "/map"; _traj_vis.ns = "traj_node/trajectory_waypoints"; _traj_vis.id = 0; _traj_vis.type = visualization_msgs::Marker::SPHERE_LIST; _traj_vis.action = visualization_msgs::Marker::ADD; _traj_vis.scale.x = _vis_traj_width; _traj_vis.scale.y = _vis_traj_width; _traj_vis.scale.z = _vis_traj_width; _traj_vis.pose.orientation.x = 0.0; _traj_vis.pose.orientation.y = 0.0; _traj_vis.pose.orientation.z = 0.0; _traj_vis.pose.orientation.w = 1.0; _traj_vis.color.a = 1.0; _traj_vis.color.r = 1.0; _traj_vis.color.g = 0.0; _traj_vis.color.b = 0.0; double traj_len = 0.0; int count = 0; Vector3d cur, pre; cur.setZero(); pre.setZero(); _traj_vis.points.clear(); Vector3d pos; // 这个是在序列、点集中才会用到,指明序列中每个点的位置 geometry_msgs::Point pt; for(int i = 0; i < time.size(); i++ ) { for (double t = 0.0; t < time(i); t += 0.01, count += 1) { // 得到当前t对应的三维空间坐标位置 pos = getPosPoly(polyCoeff, i, t); cur(0) = pt.x = pos(0); cur(1) = pt.y = pos(1); cur(2) = pt.z = pos(2); _traj_vis.points.push_back(pt); if (count) traj_len += (pre - cur).norm(); pre = cur; } } _wp_traj_vis_pub.publish(_traj_vis); }
作用:获得当前时间t对应的三维空间点坐标;
Vector3d getPosPoly( MatrixXd polyCoeff, int k, double t ) { Vector3d ret; // 3 dimension: x,y,z for ( int dim = 0; dim < 3; dim++ ) { // 获取当前维度对应的polyCoeff,polyCoeff即公式中的M.inverse()*Ct*d,也即QP问题中的P矩阵,P矩阵就是x(t)多项式中各项的系数 VectorXd coeff = (polyCoeff.row(k)).segment( dim * _poly_num1D, _poly_num1D ); VectorXd time = VectorXd::Zero( _poly_num1D ); // for(int j = _poly_num1D-1; j >= 0; j--) cout << "------------" << endl; cout << polyCoeff << endl; cout << coeff << endl; cout << "------------" << endl; // x(t) = p0 + p1*t^1 + p2*t^2 + p3*t^3 + p4*t^4 + p5*t^5 + p6*t^6 + p7*t^7 for(int j = 0; j < _poly_num1D; j ++) if(j==0) // t的0次=1 time(j) = 1.0; else time(j) = pow(t, j); // 得到当前time下,各个维度对应的x(time)的值,即当前时间下对应的坐标点位置 ret(dim) = coeff.dot(time); cout << "dim:" << dim << " coeff:" << coeff << endl; } return ret; }
作用:时间分配,理论部分可参考:https://blog.csdn.net/q597967420/article/details/77623235,下面会给出原作者的代码,不过我并没完全理解,所以采用了另一种更好理解的解法;
原作者代码:
VectorXd timeAllocation( MatrixXd Path) { VectorXd time(Path.rows() - 1); /* STEP 1: Learn the "trapezoidal velocity" of "TIme Allocation" in L5, then finish this timeAllocation function variable declaration: _Vel, _Acc: _Vel = 1.0, _Acc = 1.0 in this homework, you can change these in the test.launch You need to return a variable "time" contains time allocation, which's type is VectorXd The time allocation is many relative timeline but not one common timeline */ double _Vel, _Acc; _Vel = 1.0, _Acc = 1.0; double t_slope = _Vel / _Acc; double max_s_triangle = t_slope * _Vel; std::cout <<"Path: "<< Path << std::endl; time = VectorXd::Ones(Path.rows() - 1); cout << "----------" << time << endl; for(int k = 0;k < Path.rows()-1;k++){ Vector3d p1_2 = Path.row(k) - Path.row(k+1); double s = std::sqrt(p1_2.dot(p1_2)); if(s <= max_s_triangle){ time(k) = 2.0 * s / _Acc; } else{ time(k) = (s - max_s_triangle) / _Vel + t_slope; } } std::cout <<"time: "<< time << std::endl; return time; }
另一种解法:
VectorXd timeAllocation( MatrixXd Path) { VectorXd time(Path.rows() - 1); double _Vel, _Acc; _Vel = 1.0, _Acc = 1.0; double accInv = 1.0 / _Acc; double velInv = 1.0 / _Vel; // 加速时间与距离,减速需要耗费相同的资源 double accTime = _Vel * accInv; // v = v0 + at, v0 = 0 // accDist = 0.5*_Vel*accTime = 1/2*_Vel*_Vel*accInv = 1/2*_Vel*_Vel*1/_Acc = 2/1 * _Vel^2 / _Acc double accDist = 0.5 * _Vel * accTime; // v^2 - (v0)^2 = 2ax, v0 = 0 double accTime2 = 2.0 * accTime; double accDist2 = 2.0 * accDist; // 包含加速和减速的总距离 for(int i = 0; i < Path.rows()-1; i++) { double t = 0.0; // n = norm(v) 返回向量 v 的欧几里德范数。此范数也称为 2-范数、向量模或欧几里德长度。 double dist = (Path.row(i+1) - Path.row(i)).norm(); if(dist <= accDist2) t = 2.0 * std::sqrt(dist*accInv); else t = accTime2 + (dist - accDist2)*velInv; time(i) = t; } cout << "--------------------" << time << endl; return time; }
代码来源:https://github.com/KailinTong/Motion-Planning-for-Mobile-Robots/tree/master/hw_5(从该地址下载的代码是原作者的代码,其中并未包含我后来自己写的解法部分,需要用这部分代码请从本文中直接复制替换).
相关知识来源:深蓝学院<<移动机器人运动规划>>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。