基于ROS的Minimun Snap闭式求解算法代码学习


本文是在学习路径规划算法中的一篇学习记录,由于对c++掌握粗浅,将整个代码进行了详细的梳理,方便后来者参考学习.代码需要和理论部分结合起来看更容易理解,理论部分: 《Minimun Snap轨迹优化学习记录》.

(代码注释中英夹杂,英文注释是原来作者的注释,中文注释是本人后来添加的)

1.过程演示

         1.进入到工作空间下运行catkin_make编译;
         2.运行source devel/setup.bash;
         3.打开一个新的终端运行roscore;
         4.回到原终端窗口运行roslaunch waypoint_trajectory_generator test.launch;
         5.rviz运行起来之后,按如下步骤操作:
在这里插入图片描述
         结束的操作
在这里插入图片描述
         最终的效果
在这里插入图片描述在这里插入图片描述

2.trajectory_generator_waypoint.h解析

#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

3.trajectory_generator_waypoint.cpp解析

        3.1 Factorial函数

          实现阶乘;

int TrajectoryGeneratorWaypoint::Factorial(int x)
{
    int fac = 1;
    for(int i = x; i > 0; i--)
        fac = fac * i;
    return fac;
}

        3.2 getCt函数

          下面的代码是原作者给出的,我贴到这里,但是我并没有理解作者的思路,所以后面又自己写了两种获得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;
}

        3.3 getM函数

          对于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矩阵:
在这里插入图片描述
在这里插入图片描述
         第一次循环后添加的部分:
图1
         上面M_new图中上面的红色框部分的解释:
图2
         上面M_new图中下面的红色框部分的解释:
在这里插入图片描述
         选取M_new图中的箭头指向的315.182作为例子计算一下:
在这里插入图片描述
         最终的到的M矩阵
在这里插入图片描述

        3.4 getQ函数

          对于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矩阵
在这里插入图片描述

        3.5 PolyQPGeneration函数

          作用:求解多项式系数;

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;
}

4.trajectory_generator_node.cpp解析

        4.1 rcvWaypointsCallBack函数

         作用:获得输入的所有点坐标,即演示过程中的第五部分;

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);
}

        4.2 trajGeneration函数

         作用:轨迹生成;

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);
}

        4.3 visWayPointTraj函数

         作用:可视化优化过的轨迹;

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);
}

        4.4 getPosPoly函数

         作用:获得当前时间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;
}

        4.5 timeAllocation函数

         作用:时间分配,理论部分可参考: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;
}

5.参考引用

         代码来源:https://github.com/KailinTong/Motion-Planning-for-Mobile-Robots/tree/master/hw_5(从该地址下载的代码是原作者的代码,其中并未包含我后来自己写的解法部分,需要用这部分代码请从本文中直接复制替换).

         相关知识来源:深蓝学院<<移动机器人运动规划>>

  • 8
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值