MPC自动驾驶横向控制算法实现 c++

参考博客:
(1)无人车系统(十一):轨迹跟踪模型预测控制(MPC)原理与python实现【40行代码】
(2)【自动驾驶】模型预测控制(MPC)实现轨迹跟踪
(3)自动驾驶——模型预测控制(MPC)理解与实践
(4)MPC算法学习(1)

0 前言

在这里插入图片描述
前面介绍的PID、Pure pursuit、Stanley都只是利用当前的系统误差来设计控制器。每个控制周期只选择一个目标路点作为跟踪对象,以上控制器只利用了模型进行向前一步预测。那么如果更远的未来,参考轨迹变化不是那么平缓,并且有很多弯度小(急)的部分,那么只利用一步预测很难对整条轨迹进行有效的跟踪。

为了让无人车的控制器更有远见,设计控制器必须得利用模型对未来状态进行多步预测。由此引入MPC

1 模型预测控制原理

1.1 核心思想

MPC最核心的思想就是利用三维的空间模型加上时间构成四维时空模型,然后在这个时空模型的基础上,求解最优控制器。MPC基于一段时间的时空模型,得到的控制输出也是系统在未来有限时间步的控制序列。因为理论构建的模型与系统真实模型有误差;从而,更远未来的控制输出对系统控制的价值很低,MPC仅执行输出序列的第一个控制输出
在这里插入图片描述

假设向前预测步长为 T T T,那么 T T T步的时空模型要比原始的空间模型大很多。MPC在每个控制周期都需要重新利用未来 T T T步的模型计算得到当前执行的控制指令。MPC利用了一个比原始空间模型大很多的模型仅仅只得到当前一步的最优控制器(和PID、Pure pursuit、Stanley做了同样的事)

MPC仅考虑未来几个时间步,一定程度上牺牲了最优性。

1.2 MPC优点

① 善于处理多输入多输出系统
② 可以处理约束,如安全性约束,上下阈值
③ MPC是一种向前考虑未来时间步的有限时域优化方法(一定的预测能力)
最优控制要求在整个时间优化,MPC采用了折中的策略,既不是像最优控制那样考虑整个时域,也不是仅仅考虑当前,而是考虑未来的有限时间域

1.3 MPC流程

模型预测控制在k时刻共有三步:
(1)获取系统当前的状态
(2)基于u(k),u(k+1),u(k+2),…,u(k+m)进行最优化处理
离散系统的代价函数:
J = ∑ k m − 1 E k T Q E k + u k T R u k + E N T F E N J=\sum_{k}^{m-1} E^T_kQE_k+u^T_kRu_k+E^T_NFE_N J=km1EkTQEk+ukTRuk+ENTFEN
E N E_N EN表示误差的终值,也是衡量优劣的标准
(3)只取u(k)作为控制输入施加在系统上


在下一时刻重复上面三步,在下一步进行预测时使用的就是下一步的状态值,我们将这样的方案称为滚动优化控制

1.4 MPC vs LQR
MPCLQR
研究对象线性和非线性系统均可线性系统
状态方程:离散化对非线性的状态方程进行线性化,对线性方程离散化对线性方程离散化
目标函数MPC目标函数一个是累计和LQR目标函数一个是积分
求解方法转化为二次规划问题,利用求解器进行求解,生成控制序列采用变分法,通过求解黎卡提方程进行逼近,最终获取控制序列
工作时域是求解预测时间段Np内的控制序列 ,并在下个周期后进行滚动优化求解预测时间段内的控制序列 ,只求解一次,每个周期下取对应的控制量
LQR和MPC的优缺点计算量大,实时性差,对于算力要求高,硬件成本较高① LQR不滚动优化,预测时间段内的控制序列只求解一次,没有考虑实际与规划的误差② LQR求解整个预测时域内的控制序列,而MPC可在更小的时间窗口中求解优化问题,获得次优解,可大大提升求解速度③ 无约束,假设对控制量无约束

2 MPC控制器设计

2.1 线性模型

当模型是线性的,MPC求解可以转化为一个二次规划问题
线性模型: x k + 1 = A x k + B u k + C x_{k+1}=Ax_k+Bu_k+C xk+1=Axk+Buk+C
假定未来T步的控制输入已知,为 u k , u k + 1 , u k + 2 , . . . , u k + T u_k,u_{k+1},u_{k+2},...,u_{k+T} uk,uk+1,uk+2,...,uk+T,根据以上模型与输入,可以计算未来 T 步的状态:
在这里插入图片描述
矩阵向量形式: X = A x k ​ + B u + C X=Ax _k​ +B_u+C X=Axk+Bu+C
其中,
在这里插入图片描述
在这里插入图片描述

2.2 非线性模型

线性化过程损失了模型精度。
无人车几何运动学模型:
在这里插入图片描述
将以上连续的微分模型离散化成差分模型(差分间隔为 d t d_t dt):
在这里插入图片描述
假定无人车需要跟踪的轨迹由离散点{ ( x ˉ 1 , y ˉ 1 ) , ( x ˉ 2 , y ˉ 2 ) , . . . , ( x ˉ M , y ˉ M ) (\bar{x}_1,\bar{y}_1),(\bar{x}_2,\bar{y}_2),...,(\bar{x}_M,\bar{y}_M) (xˉ1,yˉ1),(xˉ2,yˉ2),...,(xˉM,yˉM)}通过三次曲线拟合而成,可表示成由x轴为自变量的函数: y = f ( x ) = c 0 x 3 + c 1 x 2 + c 2 x + c 3 y=f(x)=c_0x^3+c_1x^2+c_2x+c_3 y=f(x)=c0x3+c1x2+c2x+c3

在每一个预测步,可以根据无人车的 x k x_k xk y k y_k yk值计算横向跟踪误差 c t e k cte_k ctek与航向偏差 e p s i k epsi_k epsik
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.3 约束

对于约束,一般分为Hard约束Soft约束。Hard约束是不可违背必须遵守的,在控制系统中,输入输出都可能会有约束限制,但是在设计时不建议将输入输出都给予Hard约束,因为这两个约束有可能会有重叠,导致优化器会产生不可行解。

建议输出采用Soft约束,而输入的话建议输入和输入参数变化率二者之间不要同时为Hard约束,可以一个Hard一个Soft。

3 MPC - 无人车轨迹跟踪

车辆的状态量偏差和控制量偏差:
x ~ = [ x ˙ − x ˙ r y ˙ − y ˙ r φ ˙ − φ ˙ r ] , u ~ = [ v − v r δ − δ r ] \tilde{\boldsymbol{x}}=\left[\begin{array}{c} \dot{x}-\dot{x}_{r} \\ \dot{y}-\dot{y}_{r} \\ \dot{\varphi}-\dot{\varphi}_{r} \end{array}\right], \tilde{\boldsymbol{u}}=\left[\begin{array}{c} v-v_{r} \\ \delta-\delta_{r} \end{array}\right] x~= x˙x˙ry˙y˙rφ˙φ˙r ,u~=[vvrδδr]

PID控制器轨迹跟随 实现博客里的运动学模型的离散状态空间方程:
x ~ ( k + 1 ) = [ 1 0 − T v r sin ⁡ φ r 0 1 T v r cos ⁡ φ r 0 0 1 ] x ~ ( k ) + [ T cos ⁡ φ r 0 T sin ⁡ φ r 0 T tan ⁡ δ r l T v r l cos ⁡ 2 δ r ] u ~ ( k ) \tilde{\boldsymbol{x}}(k+1)=\left[\begin{array}{ccc} 1 & 0 & -T v_{r} \sin \varphi_{r} \\ 0 & 1 & T v_{r} \cos \varphi_{r} \\ 0 & 0 & 1 \end{array}\right] \tilde{\boldsymbol{x}}(k)+\left[\begin{array}{cc} T \cos \varphi_{r} & 0 \\ T \sin \varphi_{r} & 0 \\ T \frac{\tan \delta_{r}}{l} & T \frac{v_{r}}{l \cos ^{2} \delta_{r}} \end{array}\right] \tilde{\boldsymbol{u}}(k) x~(k+1)= 100010TvrsinφrTvrcosφr1 x~(k)+ TcosφrTsinφrTltanδr00Tlcos2δrvr u~(k)

为了表示控制系统达到稳定控制所付出的代价,MPC控制的代价函数:
min ⁡ J ( U ) = ∑ k = 0 N − 1 ( x ~ ( k ) T Q x ~ ( k ) + u ~ ( k ) T R u ~ ( k ) ) + x ~ ( N ) T Q f x ~ ( N ) \min J(\boldsymbol{U})=\sum_{k=0}^{N-1}\left(\tilde{\boldsymbol{x}}(k)^{T} Q \tilde{\boldsymbol{x}}(k)+\tilde{\boldsymbol{u}}(k)^{T} R \tilde{\boldsymbol{u}}(k)\right)+\tilde{\boldsymbol{x}}(N)^{T} Q_{f} \tilde{\boldsymbol{x}}(N) minJ(U)=k=0N1(x~(k)TQx~(k)+u~(k)TRu~(k))+x~(N)TQfx~(N)

其中,
U = ( u 0 , u 1 , . . . , u N ) U=(u_0,u_1,...,u_N) U=(u0,u1,...,uN),并且矩阵 Q , Q f , R Q,Q_f,R Q,Qf,R为正定矩阵
Q Q Q:给定状态代价矩阵
Q f Q_f Qf:最终状态代价矩阵
R R R:输入代价矩阵
N N N:时间范围
R > 0 R>0 R>0:任何非0输入都增加 J J J的代价

对于 min ⁡ J ( U ) = ∑ k = 0 N − 1 ( x ~ ( k ) T Q x ~ ( k ) + u ~ ( k ) T R u ~ ( k ) ) + x ~ ( N ) T Q f x ~ ( N ) \min J(\boldsymbol{U})=\sum_{k=0}^{N-1}\left(\tilde{\boldsymbol{x}}(k)^{T} Q \tilde{\boldsymbol{x}}(k)+\tilde{\boldsymbol{u}}(k)^{T} R \tilde{\boldsymbol{u}}(k)\right)+\tilde{\boldsymbol{x}}(N)^{T} Q_{f} \tilde{\boldsymbol{x}}(N) minJ(U)=k=0N1(x~(k)TQx~(k)+u~(k)TRu~(k))+x~(N)TQfx~(N),需要服从的约束条件包括:
① 运动学模型约束 —— x ~ ( k + 1 ) = A x ~ ( k ) + B u ~ ( k ) \tilde{\boldsymbol{x}}(k+1)=\boldsymbol{A} \tilde{\boldsymbol{x}}(k)+\boldsymbol{B} \tilde{\boldsymbol{u}}(k) x~(k+1)=Ax~(k)+Bu~(k)
② 控制量约束 —— ∣ u ~ ( k ) ∣ ≤ u ~ max ⁡ |\tilde{\boldsymbol{u}}(k)| \leq \tilde{\boldsymbol{u}}_{\max } u~(k)u~max
③ 初始状态 —— x ~ ( 0 ) = x ~ 0 \tilde{\boldsymbol{x}}(0)=\tilde{\boldsymbol{x}}_{0} x~(0)=x~0

osqp-eigen官网给出的MPC例子
MPCExample.cpp
在这里插入图片描述

// osqp-eigen
#include "OsqpEigen/OsqpEigen.h"

// eigen
#include <Eigen/Dense>

#include <iostream>

void setDynamicsMatrices(Eigen::Matrix<double, 12, 12>& a, Eigen::Matrix<double, 12, 4>& b)
{
    a << 1., 0., 0., 0., 0., 0., 0.1, 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.1, 0., 0.,
        0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.1, 0., 0., 0., 0.0488, 0., 0., 1., 0., 0., 0.0016,
        0., 0., 0.0992, 0., 0., 0., -0.0488, 0., 0., 1., 0., 0., -0.0016, 0., 0., 0.0992, 0., 0.,
        0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0.0992, 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0.,
        0., 0., 0.9734, 0., 0., 0., 0., 0., 0.0488, 0., 0., 0.9846, 0., 0., 0., -0.9734, 0., 0., 0.,
        0., 0., -0.0488, 0., 0., 0.9846, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.9846;

    b << 0., -0.0726, 0., 0.0726, -0.0726, 0., 0.0726, 0., -0.0152, 0.0152, -0.0152, 0.0152, -0.,
        -0.0006, -0., 0.0006, 0.0006, 0., -0.0006, 0.0000, 0.0106, 0.0106, 0.0106, 0.0106, 0,
        -1.4512, 0., 1.4512, -1.4512, 0., 1.4512, 0., -0.3049, 0.3049, -0.3049, 0.3049, -0.,
        -0.0236, 0., 0.0236, 0.0236, 0., -0.0236, 0., 0.2107, 0.2107, 0.2107, 0.2107;
}

void setInequalityConstraints(Eigen::Matrix<double, 12, 1>& xMax,
                              Eigen::Matrix<double, 12, 1>& xMin,
                              Eigen::Matrix<double, 4, 1>& uMax,
                              Eigen::Matrix<double, 4, 1>& uMin)
{
    double u0 = 10.5916;

    // input inequality constraints
    uMin << 9.6 - u0, 9.6 - u0, 9.6 - u0, 9.6 - u0;

    uMax << 13 - u0, 13 - u0, 13 - u0, 13 - u0;

    // state inequality constraints
    xMin << -M_PI / 6, -M_PI / 6, -OsqpEigen::INFTY, -OsqpEigen::INFTY, -OsqpEigen::INFTY, -1.,
        -OsqpEigen::INFTY, -OsqpEigen::INFTY, -OsqpEigen::INFTY, -OsqpEigen::INFTY,
        -OsqpEigen::INFTY, -OsqpEigen::INFTY;

    xMax << M_PI / 6, M_PI / 6, OsqpEigen::INFTY, OsqpEigen::INFTY, OsqpEigen::INFTY,
        OsqpEigen::INFTY, OsqpEigen::INFTY, OsqpEigen::INFTY, OsqpEigen::INFTY, OsqpEigen::INFTY,
        OsqpEigen::INFTY, OsqpEigen::INFTY;
}

void setWeightMatrices(Eigen::DiagonalMatrix<double, 12>& Q, Eigen::DiagonalMatrix<double, 4>& R)
{
    Q.diagonal() << 0, 0, 10., 10., 10., 10., 0, 0, 0, 5., 5., 5.;
    R.diagonal() << 0.1, 0.1, 0.1, 0.1;
}

void castMPCToQPHessian(const Eigen::DiagonalMatrix<double, 12>& Q,
                        const Eigen::DiagonalMatrix<double, 4>& R,
                        int mpcWindow,
                        Eigen::SparseMatrix<double>& hessianMatrix)
{

    hessianMatrix.resize(12 * (mpcWindow + 1) + 4 * mpcWindow,
                         12 * (mpcWindow + 1) + 4 * mpcWindow);

    // populate hessian matrix
    for (int i = 0; i < 12 * (mpcWindow + 1) + 4 * mpcWindow; i++)
    {
        if (i < 12 * (mpcWindow + 1))
        {
            int posQ = i % 12;
            float value = Q.diagonal()[posQ];
            if (value != 0)
                hessianMatrix.insert(i, i) = value;
        } else
        {
            int posR = i % 4;
            float value = R.diagonal()[posR];
            if (value != 0)
                hessianMatrix.insert(i, i) = value;
        }
    }
}

void castMPCToQPGradient(const Eigen::DiagonalMatrix<double, 12>& Q,
                         const Eigen::Matrix<double, 12, 1>& xRef,
                         int mpcWindow,
                         Eigen::VectorXd& gradient)
{

    Eigen::Matrix<double, 12, 1> Qx_ref;
    Qx_ref = Q * (-xRef);

    // populate the gradient vector
    gradient = Eigen::VectorXd::Zero(12 * (mpcWindow + 1) + 4 * mpcWindow, 1);
    for (int i = 0; i < 12 * (mpcWindow + 1); i++)
    {
        int posQ = i % 12;
        float value = Qx_ref(posQ, 0);
        gradient(i, 0) = value;
    }
}

void castMPCToQPConstraintMatrix(const Eigen::Matrix<double, 12, 12>& dynamicMatrix,
                                 const Eigen::Matrix<double, 12, 4>& controlMatrix,
                                 int mpcWindow,
                                 Eigen::SparseMatrix<double>& constraintMatrix)
{
    constraintMatrix.resize(12 * (mpcWindow + 1) + 12 * (mpcWindow + 1) + 4 * mpcWindow,
                            12 * (mpcWindow + 1) + 4 * mpcWindow);

    // populate linear constraint matrix
    for (int i = 0; i < 12 * (mpcWindow + 1); i++)
    {
        constraintMatrix.insert(i, i) = -1;
    }

    for (int i = 0; i < mpcWindow; i++)
        for (int j = 0; j < 12; j++)
            for (int k = 0; k < 12; k++)
            {
                float value = dynamicMatrix(j, k);
                if (value != 0)
                {
                    constraintMatrix.insert(12 * (i + 1) + j, 12 * i + k) = value;
                }
            }

    for (int i = 0; i < mpcWindow; i++)
        for (int j = 0; j < 12; j++)
            for (int k = 0; k < 4; k++)
            {
                float value = controlMatrix(j, k);
                if (value != 0)
                {
                    constraintMatrix.insert(12 * (i + 1) + j, 4 * i + k + 12 * (mpcWindow + 1))
                        = value;
                }
            }

    for (int i = 0; i < 12 * (mpcWindow + 1) + 4 * mpcWindow; i++)
    {
        constraintMatrix.insert(i + (mpcWindow + 1) * 12, i) = 1;
    }
}

void castMPCToQPConstraintVectors(const Eigen::Matrix<double, 12, 1>& xMax,
                                  const Eigen::Matrix<double, 12, 1>& xMin,
                                  const Eigen::Matrix<double, 4, 1>& uMax,
                                  const Eigen::Matrix<double, 4, 1>& uMin,
                                  const Eigen::Matrix<double, 12, 1>& x0,
                                  int mpcWindow,
                                  Eigen::VectorXd& lowerBound,
                                  Eigen::VectorXd& upperBound)
{
    // evaluate the lower and the upper inequality vectors
    Eigen::VectorXd lowerInequality
        = Eigen::MatrixXd::Zero(12 * (mpcWindow + 1) + 4 * mpcWindow, 1);
    Eigen::VectorXd upperInequality
        = Eigen::MatrixXd::Zero(12 * (mpcWindow + 1) + 4 * mpcWindow, 1);
    for (int i = 0; i < mpcWindow + 1; i++)
    {
        lowerInequality.block(12 * i, 0, 12, 1) = xMin;
        upperInequality.block(12 * i, 0, 12, 1) = xMax;
    }
    for (int i = 0; i < mpcWindow; i++)
    {
        lowerInequality.block(4 * i + 12 * (mpcWindow + 1), 0, 4, 1) = uMin;
        upperInequality.block(4 * i + 12 * (mpcWindow + 1), 0, 4, 1) = uMax;
    }

    // evaluate the lower and the upper equality vectors
    Eigen::VectorXd lowerEquality = Eigen::MatrixXd::Zero(12 * (mpcWindow + 1), 1);
    Eigen::VectorXd upperEquality;
    lowerEquality.block(0, 0, 12, 1) = -x0;
    upperEquality = lowerEquality;
    lowerEquality = lowerEquality;

    // merge inequality and equality vectors
    lowerBound = Eigen::MatrixXd::Zero(2 * 12 * (mpcWindow + 1) + 4 * mpcWindow, 1);
    lowerBound << lowerEquality, lowerInequality;

    upperBound = Eigen::MatrixXd::Zero(2 * 12 * (mpcWindow + 1) + 4 * mpcWindow, 1);
    upperBound << upperEquality, upperInequality;
}

void updateConstraintVectors(const Eigen::Matrix<double, 12, 1>& x0,
                             Eigen::VectorXd& lowerBound,
                             Eigen::VectorXd& upperBound)
{
    lowerBound.block(0, 0, 12, 1) = -x0;
    upperBound.block(0, 0, 12, 1) = -x0;
}

double getErrorNorm(const Eigen::Matrix<double, 12, 1>& x, const Eigen::Matrix<double, 12, 1>& xRef)
{
    // evaluate the error
    Eigen::Matrix<double, 12, 1> error = x - xRef;

    // return the norm
    return error.norm();
}

int main()
{
    // set the preview window
    int mpcWindow = 20;

    // allocate the dynamics matrices
    Eigen::Matrix<double, 12, 12> a;
    Eigen::Matrix<double, 12, 4> b;

    // allocate the constraints vector
    Eigen::Matrix<double, 12, 1> xMax;
    Eigen::Matrix<double, 12, 1> xMin;
    Eigen::Matrix<double, 4, 1> uMax;
    Eigen::Matrix<double, 4, 1> uMin;

    // allocate the weight matrices
    Eigen::DiagonalMatrix<double, 12> Q;
    Eigen::DiagonalMatrix<double, 4> R;

    // allocate the initial and the reference state space
    Eigen::Matrix<double, 12, 1> x0;
    Eigen::Matrix<double, 12, 1> xRef;

    // allocate QP problem matrices and vectores
    Eigen::SparseMatrix<double> hessian;
    Eigen::VectorXd gradient;
    Eigen::SparseMatrix<double> linearMatrix;
    Eigen::VectorXd lowerBound;
    Eigen::VectorXd upperBound;

    // set the initial and the desired states
    x0 << 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0;
    xRef << 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0;

    // set MPC problem quantities
    setDynamicsMatrices(a, b);
    setInequalityConstraints(xMax, xMin, uMax, uMin);
    setWeightMatrices(Q, R);

    // cast the MPC problem as QP problem
    castMPCToQPHessian(Q, R, mpcWindow, hessian);
    castMPCToQPGradient(Q, xRef, mpcWindow, gradient);
    castMPCToQPConstraintMatrix(a, b, mpcWindow, linearMatrix);
    castMPCToQPConstraintVectors(xMax, xMin, uMax, uMin, x0, mpcWindow, lowerBound, upperBound);

    // instantiate the solver
    OsqpEigen::Solver solver;

    // settings
    // solver.settings()->setVerbosity(false);
    solver.settings()->setWarmStart(true);

    // set the initial data of the QP solver
    solver.data()->setNumberOfVariables(12 * (mpcWindow + 1) + 4 * mpcWindow);
    solver.data()->setNumberOfConstraints(2 * 12 * (mpcWindow + 1) + 4 * mpcWindow);
    if (!solver.data()->setHessianMatrix(hessian))
        return 1;
    if (!solver.data()->setGradient(gradient))
        return 1;
    if (!solver.data()->setLinearConstraintsMatrix(linearMatrix))
        return 1;
    if (!solver.data()->setLowerBound(lowerBound))
        return 1;
    if (!solver.data()->setUpperBound(upperBound))
        return 1;

    // instantiate the solver
    if (!solver.initSolver())
        return 1;

    // controller input and QPSolution vector
    Eigen::Vector4d ctr;
    Eigen::VectorXd QPSolution;

    // number of iteration steps
    int numberOfSteps = 50;

    for (int i = 0; i < numberOfSteps; i++)
    {

        // solve the QP problem
        if (!solver.solve())
            return 1;

        // get the controller input
        QPSolution = solver.getSolution();
        ctr = QPSolution.block(12 * (mpcWindow + 1), 0, 4, 1);

        // save data into file
        auto x0Data = x0.data();

        // propagate the model
        x0 = a * x0 + b * ctr;

        // update the constraint bound
        updateConstraintVectors(x0, lowerBound, upperBound);
        if (!solver.updateBounds(lowerBound, upperBound))
            return 1;
    }
    return 0;
}

MPC.h

#pragma once

#include <iostream>
#include <vector>
#include <Eigen/Dense>
#include "KinematicModel.h"
#include "OsqpEigen/OsqpEigen.h"
#include <osqp/osqp.h>
#include "matplotlibcpp.h"

namespace plt = matplotlibcpp;
using namespace std;
using namespace Eigen;
using namespace OsqpEigen;

namespace {
    const double MAX_STEER = M_PI / 6.0;
    const double MAX_VEL = 10.0;
}
class MPC {
private:
    int NX, NU, T;
    MatrixXd R = MatrixXd::Identity(NU, NU);
    MatrixXd Rd = MatrixXd::Identity(NU, NU);
    MatrixXd Q = MatrixXd::Identity(NX, NX);
    MatrixXd Qf = Q;
public:
    MPC(int nx, int nu, int t);
    vector<double> LMPC(MatrixXd xref, Vector3d x0, MatrixXd ref_delta, KinematicModel model);
};

MPC.cpp

#include "MPC.h"

MPC::MPC(int nx, int nu, int t) : NX(nx), NU(nu), T(t) {}

vector<double> MPC::LMPC(MatrixXd xref, Vector3d x0, MatrixXd ref_delta, KinematicModel model)
{
    int NX = xref.rows();      // 3
    int NU = ref_delta.rows(); // 2
    int T = xref.cols() - 1;  // Horizon length. // 2

    // Define optimization variables.
    MatrixXd x(NX, T + 1); // 3, 3
    MatrixXd u(NU, T); // 1, 2
    // Store A matrices.
    vector<MatrixXd> A_vec;
    // Store B matrices.
    vector<MatrixXd> B_vec;

    // Initialize A and B matrices.
    for (int t = 0; t < T; ++t) {
        auto state_space = model.stateSpace(ref_delta(1, t), xref(2, t));
        A_vec.push_back(state_space[0]);
        B_vec.push_back(state_space[1]);
    }

    // Define the optimization problem.
    VectorXd cost(T + 1); // t + 1
    // List of constraint indices.
    vector<vector<int>> constraints;
    for (int t = 0; t < T; ++t) {
        cost(t) = (u.col(t) - ref_delta.col(t)).transpose() * R * (u.col(t) - ref_delta.col(t));

        if (t != 0) {
            cost(t) += (x.col(t) - xref.col(t)).transpose() * Q * (x.col(t) - xref.col(t));
        }

        MatrixXd A = A_vec[t];
        MatrixXd B = B_vec[t];
        // 3 6  6 9
        constraints.push_back({(t + 1) * NX, (t + 1) * NX + NX});  // State constraints.
        // 0 2 2 4
        constraints.push_back({t * NU, t * NU + NU});  // Input constraints.

        x.col(t + 1) = A * x.col(t) + B * (u.col(t) - ref_delta.col(t));
    }
    
    // Final state cost.
    cost(T) = (x.col(T) - xref.col(T)).transpose() * Qf * (x.col(T) - xref.col(T));

    // Set initial state.
    x.col(0) = x0; // 3

    VectorXd lower_bound(T * NU); // 4
    VectorXd upper_bound(T * NU); // 4

    for (int t = 0; t < T; t++)
    {
        lower_bound.segment(t * NU, NU) << -MAX_VEL, -MAX_STEER; // (1:2) (3:4)
        upper_bound.segment(t * NU, NU) << MAX_VEL, MAX_STEER; // (1:2) (3:4)
    }

    // Solve the optimization problem.
    OsqpEigen::Solver solver;

   // solver.settings()->setVerbosity(false);
    solver.settings()->setWarmStart(true);

    // A: T * (NU + NX) x NX * (T + 1)
   // solver.data()->setNumberOfVariables(NX * (T + 1)); // 9
   // solver.data()->setNumberOfConstraints(T * (NU + NX));// 10

    solver.data()->setNumberOfVariables(NX * (T + 1)); //变量数n 9
    solver.data()->setNumberOfConstraints(T * (NU + NX));// 约束数m 10

    // P:NX * (T + 1) x NX * (T + 1)
    // Define the Hessian matrix dimension.
    int N = NX * (T + 1); // 9

    // Define and set the Hessian matrix.
    Eigen::SparseMatrix<double> P(N, N); // 9 x 9

    for (int t = 0; t < T; ++t) {
        for (int i = 0; i < NU; ++i) {
            P.coeffRef(t * NU + i, t * NU + i) = R(i, i);
        }
    }

    if (!solver.data()->setHessianMatrix(P)) return {};

    // Define the gradient vector (cost vector).
    VectorXd q(N); // 9 x 1

    for (int t = 0; t < T; ++t) {
        q.segment(t * NU, NU) = cost(t) * VectorXd::Ones(NU);
    }

    if (!solver.data()->setGradient(q)) return {};

    // Define the linear equality constraint matrix Aeq.
    int M = T * (NX + NU);  // Number of equality constraints. // M = 10
    Eigen::SparseMatrix<double> linearMatrix(M, N);  // 10 x 9
    for (int i = 0; i < M; i++)
    {
        for (int j = 0; j < N; j++)
        {
            if (i == j) linearMatrix.insert(i, j) = 1;
            else linearMatrix.insert(i, j) = 0;
        }
    }

    if (!solver.data()->setLinearConstraintsMatrix(linearMatrix)) return {};
    // Define the equality constraint vector beq.
    VectorXd beq(M); // 10 x 1
  //  for (int i = 0; i < M; i++)
  //  {
 //       beq(i) = 0;
 //   }

    // You should populate Aeq and beq based on your state dynamics.

    // Set lower and upper bounds for variables and constraints.
    if (!solver.data()->setLowerBound(lower_bound)) {
        cerr << "Error setting lower bound." << endl;
        return {};
    }

    if (!solver.data()->setUpperBound(upper_bound)) {
        cerr << "Error setting upper bound." << endl;
        return {};
    }

    // Initialize the solver.
    if (!solver.initSolver()) return {};

    // Solve the problem.
    if (!solver.solve()) {
        cerr << "Error solving the optimization problem." << endl;
        return {};
    }
    VectorXd optimal_solution = solver.getSolution();

    // Extract optimal control inputs.
    vector<double> optimal_input;

    for (int t = 0; t < T; ++t) {
        VectorXd u_t = optimal_solution.segment(t * NU, NU); // (1,2) (3,4)
        optimal_input.push_back(u_t(0));  // Extract the velocity input.
    }
    cout << optimal_input[1] << endl;
    return optimal_input;
}

main.cpp

#include "MPC.h"
#include "Reference_path.h"
#include "KinematicModel.h"
#include "matplotlibcpp.h"
#include "pid_controller.h"

namespace plt = matplotlibcpp;

int main()
{
    parameters param;
    param.NX = 3;
    param.NU = 2;
    param.T = 2;
    param.L = 2.2;
    param.dt = 0.1;
    double Target_speed = 8.0 / 3.6;
    MPC mpc(param.NX, param.NU, param.T);

    Eigen::VectorXd x0(param.NX);

    x0 << 0.0, -3.0, 0.0; // x y yaw
    vector<double> robot_state = {0.0, -3.0, 0.0, 0.0};
    double dt = 0.1;
    double L = 2.2;
    ReferencePath reference_path;
    auto reference_trajectory = reference_path.calc_ref_trajectory(robot_state, param, 1.0);
    KinematicModel model(x0(0), x0(1), x0(2), 0.0, L, dt);
    PID_controller PID(3, 0.001, 30, Target_speed, 15.0 / 3.6, 0.0);

    std::vector<double> x_;
    std::vector<double> y_;

    for (int i = 0; i < param.T; i++)
    {
        Eigen::MatrixXd xref = reference_trajectory.xref;
        Eigen::VectorXd xref_i = xref.col(i); // 3 x 1
        Eigen::VectorXd ref_delta = reference_trajectory.dref.col(i); // 2 x 1

        std::vector<double> control_input = mpc.LMPC(xref_i, x0, ref_delta, model);
        cout << control_input[1] << endl;
        double a = PID.calOutput(model.v);

        model.updateState(a, control_input[1]);
        cout << "Speed: " << model.v << " m/s" << endl;

        x_.push_back(model.getState()[0]);
        y_.push_back(model.getState()[1]);

        const auto state = model.getState();
        x0 << state[0], state[1], state[2];

        //画参考轨迹
        plt::plot(reference_path.ref_x, reference_path.ref_y, "b--");
        plt::grid(true);
        plt::ylim(-5, 5);
        plt::plot(x_, y_, "r");
        plt::pause(0.01);
    }
    const char* filename = "./MPC.png";
    plt::save(filename);
    plt::show();
    return 0;
}

MPC.cpp有bug,有空时更正

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/398607.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

调用接口时不时出现 Error: socket hang up

项目场景&#xff1a; 提示&#xff1a;这里简述项目相关背景&#xff1a; 今天采用golang创建了一个http服务&#xff0c;准备对若干接口进行测试。 问题描述 提示&#xff1a;这里描述项目中遇到的问题&#xff1a; 在测试第一个接口时&#xff0c;发现采用postman调用接口…

PHP分析二维数据表(长度|数字字段|空值|纯姓名|英文用户名|科学计数|是否等长|是否唯一)

先看图&#xff0c;后有完整代码 <?php $t "Excel数据转Sql查询系统字段半智能分析"; $s "Excel复制过来的二维结构表内容,分析查询条件&#xff01;"; $x "字段|最大长度|长度有|数字字段|空值存在|纯姓名|英文用户名|科学计数|是否等长|是否…

【Docker】docker常用命令

需要云服务器等云产品来学习Linux可以移步/-->腾讯云<--/官网&#xff0c;轻量型云服务器低至112元/年&#xff0c;新用户首次下单享超低折扣。 目录 一、镜像仓库架构图 二、docker仓库命令 1、登录仓库docker login 2、拉取镜像docker pull 3、推送镜像docker pus…

3、电源管理入门之CPU热插拔详解

目录 简介 1. 省电技术概览 (1)、cpu hotplug和idle的区别? 2. 热插拔代码介绍 (1)、为什么以cpu1为例? 3. ATF中处理 简介 之前介绍了电源的开机和关机重启,本小节开始介绍省电的技术,其中最暴力的省电方法就是直接拔核hotplug处理,就像需要…

基于Web的网文管理系统

项目&#xff1a;基于Web的网文管理系统 伴随着时代的更替&#xff0c;我国人民的生活水平在不断提高&#xff0c;同时对生活娱乐的需求也在不断加大&#xff0c;我们在日常生活中通常会以多种方式娱乐自己&#xff0c;而阅读网络小说就是其中一种方式。随着计算机技术的成熟&a…

混合精度训练篇

1 由浅入深的混合精度训练教程 为什么要用 FP16 如果我们在训练过程中将 FP32 替代为 FP16&#xff0c;有以下两个好处&#xff1a; 1. 减少显存占用&#xff1a;FP16 的显存占用只有 FP32 的一半&#xff0c;这使得我们可以用更大的 batch size&#xff1b; 2. 加速训练&a…

Word 文档中的图片另存为 .jpg 格式图片

Word 文档中的图片另存为 .jpg 格式图片 1. Office 按钮 -> 另存为2. 筛选过的网页 (*.htm;*.html)3. 查看生成文件夹References 1. Office 按钮 -> 另存为 2. 筛选过的网页 (*.htm;*.html) ​​​ 3. 查看生成文件夹 References [1] Yongqiang Cheng, https://yongq…

灾后重建中GIS技术的关键作用与案例分析

地质灾害是指全球地壳自然地质演化过程中&#xff0c;由于地球内动力、外动力或者人为地质动力作用下导致的自然地质和人类的自然灾害突发事件。由于降水、地震等自然作用下&#xff0c;地质灾害在世界范围内频繁发生。我国除滑坡灾害外&#xff0c;还包括崩塌、泥石流、地面沉…

Maxwell安装部署

1 Maxwell输出格式 database&#xff1a;变更数据所属的数据库table&#xff1a;变更数据所属的表type&#xff1a;数据变更类型ts&#xff1a;数据变更发生的时间xid&#xff1a;事务idcommit&#xff1a;事务提交标志&#xff0c;可用于重新组装事务data&#xff1a;对于inse…

政安晨:【示例演绎机器学习】【剖析神经网络】【学习核心的Keras API】

打开这篇文章&#xff0c;相信您已经了解了TensorFlow的一些基础知识&#xff0c;可以用它从头开始实现一个简单模型。 如果您对这些概念还不是太清晰&#xff0c;可以浏览一下我这个栏目中的相关文章&#xff1a; 政安晨的机器学习笔记http://t.csdnimg.cn/DHcyL 尤其是其中…

C 语言 devc++ 使用 winsock 实现 windows UDP 利用 IP 进行局域网发送消息

UDP 通信流程_udp通信过程-CSDN博客参考来源 UDP 通信流程_udp通信过程-CSDN博客 这里移植到windows 上 &#xff0c;使用 devc 开发。 服务端代码 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <…

爬虫知识--02

免费代理池搭建 # 代理有免费和收费代理 # 代理有http代理和https代理 # 匿名度&#xff1a; 高匿&#xff1a;隐藏访问者ip 透明&#xff1a;服务端能拿到访问者ip 作为后端&#xff0c;如何拿到使用代理人的ip 请求头中&#xff1a;x-forwor…

基于芯驰 X9HP PTG4.1 在 yocto 中添加 Linux 应用

1.参考例程并添加应用 1.1 参考例程 &#xff08;1&#xff09;查看自带的串口测试例程 uart_test &#xff0c;查看 bb 文件怎么写的。 1.2 添加 printf-test 应用 &#xff08;1&#xff09;在 yocto/meta-semidrive/recipes-bsp/ 目录中 copy 自带例程 uart-test 改名为 …

Java后端底座从无到有的搭建(随笔)

文章目录 开发模式的演变草创时期1.0时期&#xff08;基座时期&#xff09;1.1时期&#xff08;低代码时期&#xff09;2.0时期&#xff08;无代码时期&#xff09; 前言&#xff1a;本文是笔者在初创公司&#xff0c;一年多来Java后端服务底座搭建过程的总结&#xff0c;如有不…

notepad++的下载与使用

1.进入官网下载 https://notepad-plus-plus.org/ 点击下载即可 2.选择中文简体 3.建议安装在D盘 其余步骤按照指示就行 4.安装后这几个是必选的 设置完成后就可以写中文了 以此为例 结果为

实例分析AnnexB格式h264流startcode

我们知道&#xff0c;h264 流格式有两种&#xff1a;avcC与AnnexB。 avcC 就是在 NALU 前面写上几个字节&#xff0c;这几个字节组成一个整数&#xff08;大端字节序&#xff09;这个整数表示了整个 NALU 的长度。在读取的时候&#xff0c;先把这个整数读出来&#xff0c;拿到…

django rest framework 学习笔记-实战商城

01项目环境搭建_哔哩哔哩_bilibili 本博客借鉴至大佬的视频学习笔记 # 创建项目 django-admin startproject MyShop# 创建app E:\desktop\my_drf\MyShop>django-admin startapp goodsE:\desktop\my_drf\MyShop>django-admin startapp orderE:\desktop\my_drf\MyShop>…

MongoDB 权限管理

文章目录 前言1. 权限控制1.1 MongoDB 默认角色1.1.1 读写角色1.1.2 管理角色1.1.3 其他角色1.1.4 超级用户角色 1.2 用户管理1.2.1 查看用户1.2.2 创建新用户1.2.3 调整角色1.2.4 删除用户1.2.4 修改密码 前言 上一篇 《MongoDB 单机安装部署》 文章中&#xff0c;为 MongoDB…

MySQL安装教程(详细版)

今天分享的是Win10系统下MySQL的安装教程&#xff0c;打开MySQL官网&#xff0c;按步骤走呀~ 宝们安装MySQL后&#xff0c;需要简单回顾一下关系型数据库的介绍与历史&#xff08;History of DataBase&#xff09; 和 常见关系型数据库产品介绍 呀&#xff0c;后面就会进入正式…

Pytorch学习05_常见的transforms01

案例解释 创建新的py文件 引入transforms模块 from torchvision import transforms 按住”Ctrl“&#xff0c;鼠标左键点击”transforms“&#xff0c;跳转到”__init__.py“ 再次按住”Ctrl“&#xff0c;鼠标点击”.transforms”&#xff0c;跳转到transforms.py中 __call_…