参考博客: (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=k∑m−1​EkT​QEk​+ukT​Ruk​+ENT​FEN​

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)=c0​x3+c1​x2+c2​x+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˙r​y˙​−y˙​r​φ˙​−φ˙​r​​

​,u~=[v−vr​δ−δ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)=

​100​010​−Tvr​sinφr​Tvr​cosφr​1​

​x~(k)+

​Tcosφr​Tsinφr​Tltanδr​​​00Tlcos2δr​vr​​​

​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=0N−1​(x~(k)TQx~(k)+u~(k)TRu~(k))+x~(N)TQf​x~(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=0N−1​(x~(k)TQx~(k)+u~(k)TRu~(k))+x~(N)TQf​x~(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

#include

void setDynamicsMatrices(Eigen::Matrix& a, Eigen::Matrix& 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& xMax,

Eigen::Matrix& xMin,

Eigen::Matrix& uMax,

Eigen::Matrix& 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& Q, Eigen::DiagonalMatrix& 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& Q,

const Eigen::DiagonalMatrix& R,

int mpcWindow,

Eigen::SparseMatrix& 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& Q,

const Eigen::Matrix& xRef,

int mpcWindow,

Eigen::VectorXd& gradient)

{

Eigen::Matrix 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& dynamicMatrix,

const Eigen::Matrix& controlMatrix,

int mpcWindow,

Eigen::SparseMatrix& 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& xMax,

const Eigen::Matrix& xMin,

const Eigen::Matrix& uMax,

const Eigen::Matrix& uMin,

const Eigen::Matrix& 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& 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& x, const Eigen::Matrix& xRef)

{

// evaluate the error

Eigen::Matrix error = x - xRef;

// return the norm

return error.norm();

}

int main()

{

// set the preview window

int mpcWindow = 20;

// allocate the dynamics matrices

Eigen::Matrix a;

Eigen::Matrix b;

// allocate the constraints vector

Eigen::Matrix xMax;

Eigen::Matrix xMin;

Eigen::Matrix uMax;

Eigen::Matrix uMin;

// allocate the weight matrices

Eigen::DiagonalMatrix Q;

Eigen::DiagonalMatrix R;

// allocate the initial and the reference state space

Eigen::Matrix x0;

Eigen::Matrix xRef;

// allocate QP problem matrices and vectores

Eigen::SparseMatrix hessian;

Eigen::VectorXd gradient;

Eigen::SparseMatrix 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

#include

#include

#include "KinematicModel.h"

#include "OsqpEigen/OsqpEigen.h"

#include

#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 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 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 A_vec;

// Store B matrices.

vector 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> 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 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 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 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 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 x_;

std::vector 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 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,有空时更正

推荐阅读

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。