MPC的横向控制与算法仿真实现

news2024/11/17 17:51:07

文章目录

    • 1. 引言
    • 2. 模型预测控制(MPC)
      • 2.1 基础知识
      • 2.2 MPC的整体流程
      • 2.3 MPC的设计求解
    • 3. 车辆运动学MPC设计
    • 4. 算法和仿真实现

1. 引言

随着智能交通系统和自动驾驶技术的发展,车辆的横向控制成为了研究的热点。横向控制指的是对车辆在行驶过程中的水平运动进行控制,包括车辆的转向、车道保持、避障等。这些控制任务对于提高道路安全性、减少交通事故、提升驾驶舒适性具有重要意义。模型预测控制(Model Predictive Control, MPC)作为一种先进的控制策略,因其在处理多变量系统、非线性系统以及约束条件下的优越性能,被广泛应用于车辆横向控制领域。

2. 模型预测控制(MPC)

2.1 基础知识

二次规划(Quadratic Programming, QP)是数学优化中的一个重要分支,它涉及寻找一个使得二次函数达到最小值的变量向量的优化问题。这类问题广泛应用于经济学、工程学、机器学习等领域。

二次规划问题的一般形式可以表示为:

mini u J = 1 2 u T H u + u T f subject to M l e u ≤ b l e M e q u = b e q u m i n ≤ u ≤ u m a x (1) \begin{align*} \underset{u}{\text{mini}} \quad & J = \frac{1}{2} \mathbf{u}^T \mathbf{H} \mathbf{u} + \mathbf{u}^T \mathbf{f} \\ \text{subject to} \quad & \mathbf{{M_{le}}u} \leq \mathbf{b_{le}} \\ & \mathbf{M_{eq}u} = \mathbf{b_{eq}} \\ & \mathbf{u_{min}} \leq \mathbf{u} \leq \mathbf{u_{max}} \end{align*}\tag{1} uminisubject toJ=21uTHu+uTfMleubleMequ=bequminuumax(1)

其中:

  • u \mathbf{u} u是决策变量的向量;

  • H \mathbf{H} H是一个对称正定矩阵,表示二次项的系数;

  • f \mathbf{f} f是线性项的系数向量;

  • M l e \mathbf{M_{le}} Mle b l e \mathbf{b_{le}} ble分别是不等式约束的系数矩阵和常数向量;

  • M e q \mathbf{M_{eq}} Meq b e q \mathbf{b_{eq}} beq分别是等式约束的系数矩阵和常数向量;

  • u m i n \mathbf{u_{min}} umin u m a x \mathbf{u_{max}} umax是变量向量 u \mathbf{u} u的取值范围的上限向量和下限向量。

:当 H \mathbf{H} H为正定矩阵时,目标函数存在全局唯一最优解, H \mathbf{H} H半正定时,目标函数存在全局最优解(不唯一), H \mathbf{H} H为不定矩阵,目标函数非凸,存在多个局部最小值和稳定点。

解决二次规划问题通常有多种方法,包括但不限于:

  1. 解析方法:对于一些特殊形式的二次规划问题,可以通过解析方法直接求得最优解。例如,当 Q Q Q矩阵是正定时,问题可以通过求解相应的线性规划问题来简化。

  2. 迭代方法:对于更一般的问题,可以使用迭代方法来求解。这类方法包括内点法、梯度投影法、序列二次规划(Sequential Quadratic Programming, SQP)等。

  3. 软件包:存在多种优化软件包可以用于求解二次规划问题,如MATLAB的quadprog函数,Python的cvxpy库,以及专业的数学优化软件如Gurobi和CPLEX等。

在实际应用中,选择合适的方法取决于问题的规模、结构和特定要求。对于大规模或非常复杂的问题,通常需要使用专业的优化软件和算法来求解。

二次规划问题在实际中的重要性体现在其模型能够很好地描述现实世界中的许多优化问题,尤其是在目标函数包含二次项时。例如,在资源分配、投资组合优化、模型预测控制、机器学习中的支持向量机(SVM)模型训练等问题中,二次规划都扮演着关键角色。在模型预测控制中,我们需要在每个时刻通过预测模型来计算未来一段时间内的最优控制输入,而这个过程可以转化为一个带有约束的二次规划问题,进而使用二次规划求解方法求出最优控制结果。

2.2 MPC的整体流程

模型预测控制是一种先进的控制策略,它基于系统模型来预测未来的系统行为,并在此基础上优化控制输入。MPC的核心思想是在每一个控制迭代中,解决一个有限时间范围内的优化问题,以实现对系统未来行为的预测和控制。考虑到系统的不确定性、测量误差等因素,在实际的控制应用中,通常会选取预测区间内最优控制序列的第一项作为当前时刻的控制输入。

MPC的基本步骤包括:

  1. 系统模型:建立一个描述系统动态行为的数学模型。这个模型通常是系统的微分方程或差分方程,用于预测系统状态随时间的变化。
  2. 预测:在当前时间点,使用系统模型预测未来一段时间内(称为预测视界)的系统状态。预测通常考虑到未来的控制输入和系统的初始状态。
  3. 目标函数和约束:定义一个目标函数,用于评估系统状态和控制输入的性能。目标函数通常包括跟踪误差和控制努力的加权和。同时,定义一系列约束条件,包括系统的物理约束(如速度、加速度的上下限)、操作约束(如控制输入的变化率)和安全约束(如避免碰撞)。
  4. 优化问题:在每个控制迭代中,基于当前状态和预测模型,求解一个优化问题以确定最优的控制序列。优化问题是在满足所有约束条件的前提下,最小化目标函数。
  5. 反馈校正:从优化问题的解中提取当前时刻的控制输入,并将其应用于系统。在下一个控制迭代,根据新的系统状态重复上述过程。

2.3 MPC的设计求解

模型预测控制器多为数字控制,因此多应用于离散系统,这里我们使用离散线性时不变系统为例,假设其系统状态方程为

x k + 1 = A x k + B u k (2) \mathbf{x}_{k+1}=\mathbf{A}\mathbf{x}_{k}+\mathbf{B}\mathbf{u}_{k} \tag{2} xk+1=Axk+Buk(2)

其中 x k \mathbf{x}_{k} xk m × 1 m\times1 m×1的状态向量, A \mathbf{A} A m × m m\times m m×m的状态矩阵, B \mathbf{B} B n × n n\times n n×n的输入矩阵, u k \mathbf{u}_{k} uk n × 1 n\times1 n×1的输入向量,其对应的二次型性能指标可以定义为

J = ∑ k = 0 N − 1 ( x k T Q x k + u k T R u k ) + x N T F x N (3) J=\sum_{k=0}^{N-1} (\mathbf{x}_k^T \mathbf{Q} \mathbf{x}_k + \mathbf{u}_k^T \mathbf{R} \mathbf{u}_k)+\mathbf{x}^T_N\mathbf{F}\mathbf{x}_N \tag{3} J=k=0N1(xkTQxk+ukTRuk)+xNTFxN(3)

其中 Q \mathbf {Q} Q R \mathbf{R} R F \mathbf{F} F分别为 m × m m \times m m×m的运行状态代价的对称矩阵、 n × n n \times n n×n的输入代价的对称矩阵和 m × m m \times m m×m的末端状态代价的对称矩阵。

我们将 k k k时刻在 k + i k+i k+i时的预测输入值记为 u k + i ∣ k \mathbf{u}_{k+i|k} uk+ik,其中 k = 0 , 1 , … , N p − 1 k = 0, 1, \ldots,N_p-1 k=0,1,,Np1 N p N_p Np为预测视界,即预测区间。同理其对应的的预测输出 x k + i ∣ k \mathbf{x}_{k+i|k} xk+ik

设系统预测视界为 N p N_p Np时间段内的预测控制输入为 u k , u k + 1 ∣ k , … , u k + N p − 1 ∣ k \mathbf{u}_k,\mathbf{u}_{k+1|k},\dots,\mathbf{u}_{k+N_p-1|k} uk,uk+1∣k,,uk+Np1∣k,根据2式可得

x k + 1 ∣ k = A x k ∣ k + B u k ∣ k x k + 2 ∣ k = A x k + 1 ∣ k + B u k + 1 ∣ k = A ( A x k ∣ k + B u k ∣ k ) + B u k + 1 ∣ k = A 2 x k ∣ k + A B u k ∣ k + B u k + 1 ∣ k x k + 3 ∣ k = A x k + 2 ∣ k + B u k + 2 ∣ k = A ( A 2 x k ∣ k + A B u k ∣ k + B u k + 1 ∣ k ) + B u k + 2 ∣ k = A 3 x k ∣ k + A 2 B u k ∣ k + A B u k + 1 ∣ k + B u k + 2 ∣ k … x k + N p = A N p x k ∣ k + A N p − 1 B u k ∣ k + ⋯ + A B u k + N p − 2 ∣ k + B u k + N p − 1 ∣ k (4) \begin{align*} \mathbf{x}_{k+1|k}&=\mathbf{Ax}_{k|k}+\mathbf{Bu}_{k|k}\\ \mathbf{x}_{k+2|k}&=\mathbf{Ax}_{k+1|k}+\mathbf{Bu}_{k+1|k}\\ &=\mathbf{A}(\mathbf{Ax}_{k|k}+\mathbf{Bu}_{k|k})+\mathbf{Bu}_{k+1|k}\\ &=\mathbf{A^2x}_{k|k}+\mathbf{ABu}_{k|k}+\mathbf{Bu}_{k+1|k}\\ \mathbf{x}_{k+3|k}&=\mathbf{Ax}_{k+2|k}+\mathbf{Bu}_{k+2|k}\\ &=\mathbf{A}(\mathbf{A^2x}_{k|k}+\mathbf{ABu}_{k|k}+\mathbf{Bu}_{k+1|k})+\mathbf{Bu}_{k+2|k}\\ &=\mathbf{A^3x}_{k|k}+\mathbf{A^2Bu}_{k|k}+\mathbf{ABu}_{k+1|k}+\mathbf{Bu}_{k+2|k}\\ \dots\\ \mathbf{x}_{k+N_p}&=\mathbf{A^{N_p}x}_{k|k}+\mathbf{A^{N_p-1}Bu}_{k|k}+\dots+\mathbf{ABu}_{k+N_p-2|k}+\mathbf{B}u_{k+N_p-1|k}\\ \end{align*}\tag{4} xk+1∣kxk+2∣kxk+3∣kxk+Np=Axkk+Bukk=Axk+1∣k+Buk+1∣k=A(Axkk+Bukk)+Buk+1∣k=A2xkk+ABukk+Buk+1∣k=Axk+2∣k+Buk+2∣k=A(A2xkk+ABukk+Buk+1∣k)+Buk+2∣k=A3xkk+A2Bukk+ABuk+1∣k+Buk+2∣k=ANpxkk+ANp1Bukk++ABuk+Np2∣k+Buk+Np1∣k(4)

将上式转化为矩阵向量形式,可得预测空间内系统的状态方程为:

[ x k + 1 ∣ k x k + 2 ∣ k … x k + N P ∣ k ] = [ A A 2 … A N p ] x k ∣ k + [ B 0 … 0 A B B … 0 ⋮ ⋮ ⋱ ⋮ A N p − 1 B A N p − 2 B … 0 ] [ u k ∣ k u k + 1 ∣ k … u k + N p − 1 ∣ k ] (5) \begin{align*} \begin{bmatrix} \mathbf{x}_{k+1|k}\\ \mathbf{x}_{k+2|k}\\ \dots\\ \mathbf{x}_{k+N_P|k}\end{bmatrix}= \begin{bmatrix} \mathbf{A}\\ \mathbf{A^2}\\ \dots\\ \mathbf{A^{N_p}} \end{bmatrix} \mathbf{x}_{k|k} + \begin{bmatrix} \mathbf{B} & \mathbf{0} & \dots &\mathbf{0}\\ \mathbf{AB} & \mathbf{B} & \dots &\mathbf{0}\\ \vdots & \vdots & \ddots & \vdots\\ \mathbf{A^{N_p-1}B} & \mathbf{A^{N_p-2}B} & \dots &\mathbf{0}\\ \end{bmatrix} \begin{bmatrix} \mathbf{u}_{k|k}\\ \mathbf{u}_{k+1|k}\\ \dots\\ \mathbf{u}_{k+N_p-1|k}\\ \end{bmatrix} \end{align*}\tag{5} xk+1∣kxk+2∣kxk+NPk = AA2ANp xkk+ BABANp1B0BANp2B000 ukkuk+1∣kuk+Np1∣k (5)

为了简化后面的分析,我们定义

X k = [ x k + 1 ∣ k x k + 2 ∣ k … x k + N P ∣ k ] M = [ A A 2 … A N p ] C = [ B 0 … 0 A B B … 0 ⋮ ⋮ ⋱ ⋮ A N p − 1 B A N p − 2 B … 0 ] U k = [ u k ∣ k u k + 1 ∣ k … u k + N p − 1 ∣ k ] (6) \begin{align*} \mathbf{X}_k&= \begin{bmatrix} \mathbf{x}_{k+1|k}\\ \mathbf{x}_{k+2|k}\\ \dots\\ \mathbf{x}_{k+N_P|k} \end{bmatrix} \\ \mathbf{M}&= \begin{bmatrix} \mathbf{A}\\ \mathbf{A^2}\\ \dots\\ \mathbf{A^{N_p}} \end{bmatrix}\\ \mathbf{C}&= \begin{bmatrix} \mathbf{B} & \mathbf{0} & \dots &\mathbf{0}\\ \mathbf{AB} & \mathbf{B} & \dots &\mathbf{0}\\ \vdots & \vdots & \ddots & \vdots\\ \mathbf{A^{N_p-1}B} & \mathbf{A^{N_p-2}B} & \dots &\mathbf{0}\\ \end{bmatrix} \\ \mathbf{U}_k&= \begin{bmatrix} \mathbf{u}_{k|k}\\ \mathbf{u}_{k+1|k}\\ \dots\\ \mathbf{u}_{k+N_p-1|k}\\ \end{bmatrix} \end{align*}\tag{6} XkMCUk= xk+1∣kxk+2∣kxk+NPk = AA2ANp = BABANp1B0BANp2B000 = ukkuk+1∣kuk+Np1∣k (6)

则5式可以记为

X k = M x k ∣ k + C U k (7) \mathbf{X}_k=\mathbf{M}\mathbf{x}_{k|k}+\mathbf{C}\mathbf{U}_{k}\tag{7} Xk=Mxkk+CUk(7)

结合3式的代价函数,可得在 k k k时刻,当预测区间为 N p N_p Np的时候,该预测区间内的性能指标为

J p = ∑ i = 0 N p − 1 ( x k + i ∣ k T Q x k + i ∣ k + u k + i ∣ k T R u k + i ∣ k ) + x k + N p ∣ k T F x k + N p ∣ k = ∑ i = 0 N p − 1 ( x k + i ∣ k T Q x k + i ∣ k ) + ∑ i = 0 N p − 1 ( u k + i ∣ k T R u k + i ∣ k ) + x k + N p ∣ k T F x k + N p ∣ k = x k ∣ k T Q x k ∣ k + ∑ i = 1 N p − 1 ( x k + i ∣ k T Q x k + i ∣ k ) + ∑ i = 0 N p − 1 ( u k + i ∣ k T R u k + i ∣ k ) + x k + N p ∣ k T F x k + N p ∣ k (8) \begin{align*} J_p&=\sum_{i=0}^{N_p-1} (\mathbf{x}_{k+i|k}^T \mathbf{Q} \mathbf{x}_{k+i|k} + \mathbf{u}_{k+i|k}^T \mathbf{R} \mathbf{u}_{k+i|k})+\mathbf{x}^T_{k+N_p|k}\mathbf{F}\mathbf{x}_{k+N_p|k}\\ &=\sum_{i=0}^{N_p-1} (\mathbf{x}_{k+i|k}^T \mathbf{Q} \mathbf{x}_{k+i|k})+\sum_{i=0}^{N_p-1} (\mathbf{u}_{k+i|k}^T \mathbf{R} \mathbf{u}_{k+i|k})+\mathbf{x}^T_{k+N_p|k}\mathbf{F}\mathbf{x}_{k+N_p|k}\\ &=\mathbf{x}_{k|k}^T \mathbf{Q} \mathbf{x}_{k|k}+\sum_{i=1}^{N_p-1} (\mathbf{x}_{k+i|k}^T \mathbf{Q} \mathbf{x}_{k+i|k})+\sum_{i=0}^{N_p-1} (\mathbf{u}_{k+i|k}^T \mathbf{R} \mathbf{u}_{k+i|k})+\mathbf{x}^T_{k+N_p|k}\mathbf{F}\mathbf{x}_{k+N_p|k} \end{align*}\tag{8} Jp=i=0Np1(xk+ikTQxk+ik+uk+ikTRuk+ik)+xk+NpkTFxk+Npk=i=0Np1(xk+ikTQxk+ik)+i=0Np1(uk+ikTRuk+ik)+xk+NpkTFxk+Npk=xkkTQxkk+i=1Np1(xk+ikTQxk+ik)+i=0Np1(uk+ikTRuk+ik)+xk+NpkTFxk+Npk(8)

因为 x k ∣ k \mathbf{x}_{k|k} xkk为系统初对应的就是 k k k时刻的系统状态,即为当前时刻 k k k的初始状态,是已知量,因此后面的 x k + i + 1 ∣ k \mathbf{x}_{k+i+1|k} xk+i+1∣k可以通过2式用 u k + i ∣ k \mathbf{u}_{k+i|k} uk+ik x k + i ∣ k \mathbf{x}_{k+i|k} xk+ik来表达,因此 J p J_p Jp的大小仅与输入变量 u \mathbf{u} u有关,此时最优问题已经转换为二次规划问题,使用现有的二次规划进行求解,获取预测区间内的最优序列,然后取第一个最优控制作为当前系统的输入。

注:对于8式不需要把 x k + i + 1 ∣ k \mathbf{x}_{k+i+1|k} xk+i+1∣k u k + i − 1 ∣ k \mathbf{u}_{k+i-1|k} uk+i1∣k去展开表示,添加2式的等式约束就可以实现这个效果,详见后面的代码实现。

3. 车辆运动学MPC设计

在《车辆运动学模型的线性化和离散化及代码实现》中,我们详细介绍了单车模型的线性化和离散化,其离散线性化后的微分方程如下

x e ( k + 1 ) = A e x e ( k ) + B e u e ( k ) = A e ( x ( k ) − x r e f ( k ) ) + B e ( u e ( k ) − u r e f ( k ) ) = [ 1 0 − T v r s i n φ r 0 1 T v r c o s φ r 0 0 1 ] [ x − x r y − y r φ − φ r ] + [ T c o s φ r 0 T s i n φ r 0 T v r t a n δ f r L T v r L c o s 2 δ f r ] [ v − v r δ − δ r ] (9) \begin{align*} \mathbf{x_e}(k+1) &=\mathbf{A_e}\mathbf{x_e}(k)+\mathbf{B_e}\mathbf{u_e}(k)\\ &=\mathbf{A_e}(\mathbf{x}(k)-\mathbf{x_ref}(k))+\mathbf{B_e}(\mathbf{u_e}(k)-\mathbf{u_{ref}}(k))\\ &= \begin{bmatrix} 1 & 0 & -Tv_rsin\varphi_r\\ 0 & 1 & Tv_rcos\varphi_r\\ 0 & 0 & 1\\ \end{bmatrix} \begin{bmatrix} x-x_r\\ y-y_r\\ \varphi-\varphi_r\\ \end{bmatrix} + \begin{bmatrix} Tcos\varphi_r & 0 \\ Tsin\varphi_r & 0 \\ \frac{Tv_r tan\delta_{fr}}{L} & \frac{Tv_r}{Lcos^2\delta_{fr}} \\ \end{bmatrix} \begin{bmatrix} v-v_r\\ \delta-\delta_r\\ \end{bmatrix} \\ \end{align*} \tag{9} xe(k+1)=Aexe(k)+Beue(k)=Ae(x(k)xref(k))+Be(ue(k)uref(k))= 100010TvrsinφrTvrcosφr1 xxryyrφφr + TcosφrTsinφrLTvrtanδfr00Lcos2δfrTvr [vvrδδr](9)

其中 T T T为采样步长,定义代价函数
J = ∑ k = 0 N − 1 ( x k T Q x k + u k T R u k ) + x N T F x N (10) J = \sum_{k=0}^{N-1} (\mathbf{x}_k^T \mathbf{Q} \mathbf{x}_k + \mathbf{u}_k^T \mathbf{R} \mathbf{u}_k) + \mathbf{x}_N^T \mathbf{F} \mathbf{x}_N \tag{10} J=k=0N1(xkTQxk+ukTRuk)+xNTFxN(10)

其中:

  • x k \mathbf{x}_k xk 是在离散时间步 k k k的系统状态,即车辆在 k k k时刻的状态。
  • u k \mathbf{u}_k uk是在时间步 k k k的控制输入。
  • x r e f k \mathbf{x_{ref}}_k xrefk 根据参考线计算出来在离散时间步 k k k的理想系统状态。
  • u r e f k \mathbf{u_{ref}}_k urefk是根据参考线在时间步 k k k的理想控制输入。
  • Q \mathbf{Q} Q是状态权重 m × m m \times m m×m的半正定方阵,用于衡量状态的代价,通常将其设计为对角矩阵。
  • R \mathbf{R} R是控制权重 n × n n \times n n×n的正定对称矩阵,用于衡量控制输入的代价,通常将其设计为对角矩阵。
  • F \mathbf{F} F是末端状态权重 m × m m \times m m×m的半正定方阵,用于衡量最终状态的代价,通常将其设计为对角矩阵。
  • N \mathbf{N} N是控制的总时间步数。

根据第二节的推导,我们可以得到其对应的预测空间 N p N_p Np内的状态方程和代价函数如下
mini u J p = x e k ∣ k T Q x e k ∣ k + ∑ i = 1 N p − 1 ( x e k + i ∣ k T Q x e k + i ∣ k ) + ∑ i = 0 N p − 1 ( u e k + i ∣ k T R u e k + i ∣ k ) + x e k + N p ∣ k T F x e k + N p ∣ k subject to x e ( k + 1 ) = A e x e ( k ) + B e u e ( k ) x e ( 0 ) = x e 0 ∣ u ∣ ≤ u m a x (11) \begin{align*} \underset{u}{\text{mini}}\quad J_p&=\mathbf{x_e}_{k|k}^T \mathbf{Q} \mathbf{x_e}_{k|k}+\sum_{i=1}^{N_p-1} (\mathbf{x_e}_{k+i|k}^T \mathbf{Q} \mathbf{x_e}_{k+i|k})+\sum_{i=0}^{N_p-1} (\mathbf{u_e}_{k+i|k}^T \mathbf{R} \mathbf{u_e}_{k+i|k})+\mathbf{x_e}^T_{k+N_p|k}\mathbf{F}\mathbf{x_e}_{k+N_p|k} \tag{11.1}\\ \text{subject to} \quad &\mathbf{x_e}(k+1)=\mathbf{A_e}\mathbf{x_e}(k)+\mathbf{B_e}\mathbf{u_e}(k)\tag{11.2}\\ &\mathbf{x_e}(0)=\mathbf{x_e}_0\tag{11.3}\\ &|\mathbf{u}| \leq \mathbf{u_{max}}\tag{11.4}\\ \end{align*}\tag{11} uminiJpsubject to=xekkTQxekk+i=1Np1(xek+ikTQxek+ik)+i=0Np1(uek+ikTRuek+ik)+xek+NpkTFxek+Npkxe(k+1)=Aexe(k)+Beue(k)xe(0)=xe0uumax(11)

其中

  • 公式11.2:车辆模型的运动学约束。
  • 公式11.3:初始状态约束, x e 0 \mathbf{x_e}_0 xe0为车辆当前状态,即预测区间内初始状态。
  • 公式11.4:输入量约束。

4. 算法和仿真实现

kinematicsMPC.py

import numpy as np
import math
import cvxpy as cp
from kinematic_bicycle_model import VehicleInfo, update_ABMatrix


# 系统配置
NX = 3  # 状态向量的个数: x = [x, y, yaw]
NU = 2  # 输入向量的个数: u = [v, delta]
NP = 5  # 有限时间视界长度:预测过程中考虑的时间范围的有限长度
MAX_V = 20  # 最大车速(m/s)

# MPC config
Q = np.diag([2.0, 2.0, 2.0])  # 运行状态代价
F = np.diag([2.0, 2.0, 2.0])  # 末端状态代价
R = np.diag([0.01, 0.1])  # 输入状态代价


def calc_preparation(vehicle, ref_path):
    """
    计算xref、uref、index和er
    """

    rx, ry, rv, ryaw, rkappa = ref_path[:, 0], ref_path[:, 1], ref_path[:, 2], ref_path[:, 3], ref_path[:, 5]
    dx = [vehicle.x - icx for icx in rx]
    dy = [vehicle.y - icy for icy in ry]
    d = np.hypot(dx, dy)
    index = np.argmin(d)

    vec_nr = np.array([math.cos(ryaw[index] + math.pi / 2.0),
                       math.sin(ryaw[index] + math.pi / 2.0)])
    vec_target_2_rear = np.array([vehicle.x - rx[index],
                                  vehicle.y - ry[index]])
    er = np.dot(vec_target_2_rear, vec_nr)

    xref = np.zeros((NX, NP + 1))
    uref = np.zeros((NU, NP + 1))

    for i in range(NP+1):
        ind = min(index + i, len(rx)-1)
        xref[0, i] = rx[ind]
        xref[1, i] = ry[ind]
        xref[2, i] = ryaw[ind]
        uref[0, i] = rv[ind]
        uref[1, i] = math.atan2(vehicle.L*rkappa[ind], 1)
    return xref, uref, index, er



def MPCController(vehicle, ref_path):
    xref, uref, index, er = calc_preparation(vehicle, ref_path)

    x0 = [vehicle.x, vehicle.y, vehicle.yaw]
    x = cp.Variable((NX, NP + 1))
    u = cp.Variable((NU, NP))
    cost = 0.0  # 代价函数
    constraints = []  # 约束条件

    x[:, 0] == x0
    for i in range(NP):
        cost += cp.quad_form(u[:, i] - uref[:, i], R)
        if i != 0:
            cost += cp.quad_form(x[:, i] - xref[:, i], Q)
        A, B = update_ABMatrix(vehicle, uref[1, i], xref[2, i])
        constraints += [x[:, i + 1] - xref[:, i + 1] == A @ (x[:, i] - xref[:, i]) + B @ (u[:, i] - uref[:, i])]

    cost += cp.quad_form(x[:, NP] - xref[:, NP], F)

    constraints += [(x[:, 0]) == x0]
    constraints += [cp.abs(u[0, :]) <= MAX_V]
    constraints += [cp.abs(u[1, :]) <= VehicleInfo.MAX_STEER]

    problem = cp.Problem(cp.Minimize(cost), constraints)
    problem.solve(solver=cp.ECOS, verbose=False)

    if problem.status == cp.OPTIMAL or problem.status == cp.OPTIMAL_INACCURATE:
        opt_delta = u.value[:, 0]
        return opt_delta[1], index, er

    else:
        print("Error: MPC solution failed !")
        return None, index, er

kinematic_bicycle_model.py

import math
import numpy as np

class Vehicle:
    def __init__(self,
                 x=0.0,
                 y=0.0,
                 yaw=0.0,
                 v=0.0,
                 dt=0.1,
                 l=3.0):
        self.steer = 0
        self.x = x
        self.y = y
        self.yaw = yaw
        self.v = v
        self.dt = dt
        self.L = l  # 轴距
        self.x_front = x + l * math.cos(yaw)
        self.y_front = y + l * math.sin(yaw)

    def update(self, a, delta, max_steer=np.pi):
        delta = np.clip(delta, -max_steer, max_steer)
        self.steer = delta

        self.x = self.x + self.v * math.cos(self.yaw) * self.dt
        self.y = self.y + self.v * math.sin(self.yaw) * self.dt
        self.yaw = self.yaw + self.v / self.L * math.tan(delta) * self.dt

        self.v = self.v + a * self.dt
        self.x_front = self.x + self.L * math.cos(self.yaw)
        self.y_front = self.y + self.L * math.sin(self.yaw)


class VehicleInfo:
    # Vehicle parameter
    L = 2.0  # 轴距
    W = 2.0  # 宽度
    LF = 2.8  # 后轴中心到车头距离
    LB = 0.8  # 后轴中心到车尾距离
    MAX_STEER = 0.6  # 最大前轮转角
    TR = 0.5  # 轮子半径
    TW = 0.5  # 轮子宽度
    WD = W  # 轮距
    LENGTH = LB + LF  # 车辆长度

def draw_vehicle(x, y, yaw, steer, ax, vehicle_info=VehicleInfo, color='black'):
    vehicle_outline = np.array(
        [[-vehicle_info.LB, vehicle_info.LF, vehicle_info.LF, -vehicle_info.LB, -vehicle_info.LB],
         [vehicle_info.W / 2, vehicle_info.W / 2, -vehicle_info.W / 2, -vehicle_info.W / 2, vehicle_info.W / 2]])

    wheel = np.array([[-vehicle_info.TR, vehicle_info.TR, vehicle_info.TR, -vehicle_info.TR, -vehicle_info.TR],
                      [vehicle_info.TW / 2, vehicle_info.TW / 2, -vehicle_info.TW / 2, -vehicle_info.TW / 2,
                       vehicle_info.TW / 2]])

    rr_wheel = wheel.copy()  # 右后轮
    rl_wheel = wheel.copy()  # 左后轮
    fr_wheel = wheel.copy()  # 右前轮
    fl_wheel = wheel.copy()  # 左前轮
    rr_wheel[1, :] += vehicle_info.WD / 2
    rl_wheel[1, :] -= vehicle_info.WD / 2

    # 方向盘旋转
    rot1 = np.array([[np.cos(steer), -np.sin(steer)],
                     [np.sin(steer), np.cos(steer)]])
    # yaw旋转矩阵
    rot2 = np.array([[np.cos(yaw), -np.sin(yaw)],
                     [np.sin(yaw), np.cos(yaw)]])
    fr_wheel = np.dot(rot1, fr_wheel)
    fl_wheel = np.dot(rot1, fl_wheel)
    fr_wheel += np.array([[vehicle_info.L], [-vehicle_info.WD / 2]])
    fl_wheel += np.array([[vehicle_info.L], [vehicle_info.WD / 2]])

    fr_wheel = np.dot(rot2, fr_wheel)
    fr_wheel[0, :] += x
    fr_wheel[1, :] += y
    fl_wheel = np.dot(rot2, fl_wheel)
    fl_wheel[0, :] += x
    fl_wheel[1, :] += y
    rr_wheel = np.dot(rot2, rr_wheel)
    rr_wheel[0, :] += x
    rr_wheel[1, :] += y
    rl_wheel = np.dot(rot2, rl_wheel)
    rl_wheel[0, :] += x
    rl_wheel[1, :] += y
    vehicle_outline = np.dot(rot2, vehicle_outline)
    vehicle_outline[0, :] += x
    vehicle_outline[1, :] += y

    ax.plot(fr_wheel[0, :], fr_wheel[1, :], color)
    ax.plot(rr_wheel[0, :], rr_wheel[1, :], color)
    ax.plot(fl_wheel[0, :], fl_wheel[1, :], color)
    ax.plot(rl_wheel[0, :], rl_wheel[1, :], color)

    ax.plot(vehicle_outline[0, :], vehicle_outline[1, :], color)
    ax.axis('equal')


def update_ABMatrix(vehicle, ref_delta, ref_yaw):
    """
    计算离散线性车辆运动学模型状态矩阵A和输入矩阵B
    return: A, B
    """
    A = np.matrix([
        [1.0, 0.0, -vehicle.v * vehicle.dt * math.sin(ref_yaw)],
        [0.0, 1.0, vehicle.v * vehicle.dt * math.cos(ref_yaw)],
        [0.0, 0.0, 1.0]])

    B = np.matrix([
        [vehicle.dt * math.cos(ref_yaw), 0],
        [vehicle.dt * math.sin(ref_yaw), 0],
        [vehicle.dt * math.tan(ref_delta) / vehicle.L,
         vehicle.v * vehicle.dt / (vehicle.L * math.cos(ref_delta) * math.cos(ref_delta))]])
    return A, B

path_generator.py

"""
路径轨迹生成器
"""

import math
import numpy as np

class Path:
    def __init__(self):
        self.ref_line = self.design_reference_line()
        self.ref_yaw = self.cal_yaw()
        self.ref_s = self.cal_accumulated_s()
        self.ref_kappa = self.cal_kappa()

    def design_reference_line(self):
        rx = np.linspace(0, 50, 2000) + 5 # x坐标
        ry = 20 * np.sin(rx / 20.0) + 60  # y坐标
        rv = np.full(2000, 2)
        return np.column_stack((rx, ry, rv))
    def cal_yaw(self):
        yaw = []
        for i in range(len(self.ref_line)):
            if i == 0:
                yaw.append(math.atan2(self.ref_line[i + 1, 1] - self.ref_line[i, 1],
                                 self.ref_line[i + 1, 0] - self.ref_line[i, 0]))
            elif i == len(self.ref_line) - 1:
                yaw.append(math.atan2(self.ref_line[i, 1] - self.ref_line[i - 1, 1],
                                 self.ref_line[i, 0] - self.ref_line[i - 1, 0]))
            else:
                yaw.append(math.atan2(self.ref_line[i + 1, 1] - self.ref_line[i -1, 1],
                                  self.ref_line[i + 1, 0] - self.ref_line[i - 1, 0]))
        return yaw

    def cal_accumulated_s(self):
        s = []
        for i in range(len(self.ref_line)):
            if i == 0:
                s.append(0.0)
            else:
                s.append(math.sqrt((self.ref_line[i, 0] - self.ref_line[i-1, 0]) ** 2
                                 + (self.ref_line[i, 1] - self.ref_line[i-1, 1]) ** 2))
        return s

    def cal_kappa(self):
        # 计算曲线各点的切向量
        dp = np.gradient(self.ref_line.T, axis=1)
        # 计算曲线各点的二阶导数
        d2p = np.gradient(dp, axis=1)
        # 计算曲率
        kappa = (d2p[0] * dp[1] - d2p[1] * dp[0]) / ((dp[0] ** 2 + dp[1] ** 2) ** (3 / 2))

        return kappa

    def get_ref_line_info(self):
        return self.ref_line[:, 0], self.ref_line[:, 1], self.ref_line[:, 2], self.ref_yaw, self.ref_s, self.ref_kappa

main.py

from kinematic_bicycle_model import Vehicle, VehicleInfo, draw_vehicle
from kinematicsMPC import MPCController
from path_generator import Path
import numpy as np
import matplotlib.pyplot as plt
import imageio.v2 as imageio
import sys

MAX_SIMULATION_TIME = 200.0  # 程序最大运行时间200*dt

def main():
    # 设置跟踪轨迹
    rx, ry, rv, ref_yaw, ref_s, ref_kappa = Path().get_ref_line_info()
    ref_path = np.column_stack((rx, ry, rv, ref_yaw, ref_s, ref_kappa))
    # 假设车辆初始位置为(5,60),航向角yaw=0.0,速度为2m/s,时间周期dt为0.1秒
    vehicle = Vehicle(x=5.0,
                      y=60.0,
                      yaw=0.0,
                      v=2.0,
                      dt=0.1,
                      l=VehicleInfo.L)

    time = 0.0  # 初始时间
    target_ind = 0
    # 记录车辆轨迹
    trajectory_x = []
    trajectory_y = []
    lat_err = []  # 记录横向误差

    i = 0
    image_list = []  # 存储图片
    plt.figure(1)

    last_idx = ref_path.shape[0] - 1  # 跟踪轨迹的最后一个点的索引
    while MAX_SIMULATION_TIME >= time and last_idx > target_ind:
        time += vehicle.dt  # 累加一次时间周期
        # MPC
        delta_f, target_ind, e_y = MPCController(vehicle, ref_path)
        if delta_f is None:
            print("An error occurred, exit...")
            sys.exit(1)

            # 横向误差
        lat_err.append(e_y)

        # 更新车辆状态
        vehicle.update(0.0, delta_f, np.pi / 10)  # 由于假设纵向匀速运动,所以加速度a=0.0
        trajectory_x.append(vehicle.x)
        trajectory_y.append(vehicle.y)

        # 显示动图
        plt.cla()
        plt.plot(ref_path[:, 0], ref_path[:, 1], '-.b', linewidth=1.0)
        draw_vehicle(vehicle.x, vehicle.y, vehicle.yaw, vehicle.steer, plt)

        plt.plot(trajectory_x, trajectory_y, "-r", label="trajectory")
        plt.plot(ref_path[target_ind, 0], ref_path[target_ind, 1], "go", label="target")
        plt.axis("equal")
        plt.grid(True)
        plt.pause(0.001)
        plt.savefig("temp.png")
        i += 1
        if (i % 5) == 0:
            image_list.append(imageio.imread("temp.png"))

    imageio.mimsave("display.gif", image_list, duration=0.1)

    plt.figure(2)
    plt.subplot(2, 1, 1)
    plt.plot(ref_path[:, 0], ref_path[:, 1], '-.b', linewidth=1.0)
    plt.plot(trajectory_x, trajectory_y, 'r')
    plt.title("actual tracking effect")

    plt.subplot(2, 1, 2)
    plt.plot(lat_err)
    plt.title("lateral error")
    plt.show()


if __name__ == '__main__':
    main()

运行结果:
在这里插入图片描述在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《html自用使用指南》--基于w3School实践

1.基础标签 文本输入时&#xff0c;在编辑器中的换行&#xff0c;多个空格&#xff0c;都被编辑器看作一个空格 <p> 这个段落 在源代码 中 包含 许多行 但是 浏览器 忽略了 它们。 </p>结果&#xff1a;这个段落 在源代码 中 包含 许多行 但是 浏览器…

机器学习中常见的数据分析,处理方式(以泰坦尼克号为例)

数据分析 读取数据查看数据各个参数信息查看有无空值如何填充空值一些特殊字段如何处理读取数据查看数据中的参数信息实操具体问题具体分析年龄问题 重新划分数据集如何删除含有空白值的行根据条件删除一些行查看特征和标签的相关性 读取数据 查看数据各个参数信息 查看有无空…

Python Tiler库:创建可视化网格布局的利器

更多Python学习内容&#xff1a;ipengtao.com Tiler是一个Python库&#xff0c;用于创建各种类型的网格布局&#xff0c;包括等宽/等高布局、自定义大小布局、响应式布局等。本文将深入介绍Tiler库的功能、用法以及示例代码&#xff0c;帮助读者全面了解并灵活应用该库。 安装和…

主打国产算力 广州市通用人工智能公共算力中心项目签约

4月9日&#xff0c;第十届广州国际投资年会期间&#xff0c;企商在线&#xff08;北京&#xff09;数据技术股份有限公司与广州市增城区政府就“广州市通用人工智能公共算力中心”项目进行签约。 该项目由广州市增城区人民政府发起&#xff0c;企商在线承建。项目拟建成中国最…

【Linux】实现一个进度条

我们之前也学了gcc/vim/make和makefile&#xff0c;那么我们就用它们实现一个进度条。 在实现这个进度条之前&#xff0c;我们要先简单了解一下缓冲区和回车和换行的区别 缓冲区其实就是一块内存空间&#xff0c;我们先看这样一段代码 它的现象是先立马打印&#xff0c;三秒后程…

centos7.6上安装mysql7.6 完整过程

安装过程&#xff1a; 参考&#xff1a;https://blog.csdn.net/qq_45103475/article/details/123151050 查找mysql [rootbogon ~]# whereis mysql mysql: /usr/lib64/mysql /usr/share/mysql 删除目录 [rootbogon ~]# rm -rf /usr/lib64/mysql [rootbogon ~]# whereis mysql m…

ClickHouse 高可用之副本

文章目录 ClickHouse 副本支持副本的引擎配置高可用副本副本应用1.副本表概述2.创建副本表3.写入模拟数据4.副本验证 扩展 —— 在 Zookeeper 中查看副本表信息 ClickHouse 副本 ClickHouse 通过副本机制&#xff0c;可以将数据拷贝存储在不同的节点上。这样&#xff0c;如果一…

python 如何判断两个字典是否相等

Python 字典的 cmp() 函数用于比较两个字典元素。 语法 cmp()方法语法&#xff1a; cmp(dict1, dict2)参数 dict1 -- 比较的字典。 dict2 -- 比较的字典。 返回值 如果两个字典的元素相同返回0&#xff0c;如果字典dict1大于字典dict2返回1&#xff0c;如果字典dict1小于…

白酒:香型对白酒品质的影响与消费者偏好

云仓酒庄的豪迈白酒认为香型对白酒品质的影响与消费者偏好是值得探讨的话题。香型作为白酒品质的重要因素之一&#xff0c;对白酒的口感、风味和品质产生着深远的影响。同时&#xff0c;消费者的偏好也是决定香型选择的重要因素之一。 首先&#xff0c;香型对白酒品质的影响是不…

Qt中的 tableView 设置 二进制 十六进制 序号表头

二 进制序号 因为QTableView的垂直表头并不支持使用委托来自定义。 相反&#xff0c;可以通过将自定义的QWidget作为QHeaderView的标签来实现这一目标。 代码&#xff1a; #include <QApplication> #include <QMainWindow> #include <QVBoxLayout> #include …

【管理咨询宝藏85】MBB物流战略规划及实施落地项目报告

本报告首发于公号“管理咨询宝藏”&#xff0c;如需阅读完整版报告内容&#xff0c;请查阅公号“管理咨询宝藏”。 【管理咨询宝藏85】MBB物流战略规划及实施落地项目报告 【格式】PDF版本 【关键词】战略规划、MBB、麦肯锡 【核心观点】 - 全面进行小件包裹业务实施方案的细…

编程基础“四大件”

基础四大件包括&#xff1a;数据结构和算法,计算机网络,操作系统,设计模式 这跟学什么编程语言,后续从事什么编程方向均无关&#xff0c;只要做编程开发&#xff0c;这四个计算机基础就无法避开。可以这么说&#xff0c;这基础四大件真的比编程语言重要&#xff01;&#xff0…

如何定制企业PIA问卷?

前言 大家好&#xff0c;欢迎关注用九智汇。 作为国内专业的数据合规与隐私保护工具科技服务商&#xff0c;我们近几年为多个客户提供了企业级数据合规与隐私保护体系的系统落地解决方案&#xff0c;这些客户覆盖了智能网联汽车、互联网医疗、智能硬件、金融、新零售等多个行…

【Vue】常见的七大属性(描述+案例)

一、前言 最近&#xff0c;因为项目需要自己就去学习了一下Vue的相关知识&#xff0c;自己花了几天&#xff0c;结合官方文档和相应的视频学习了一下Vue,了解了Vue大概的一些属性&#xff0c;方法&#xff0c;特点等。接下来博主会将自己学习的相关内容通过博客的形式进行记录…

基于卷积神经网络的垃圾图像分类系统研究与实现

1.摘要 垃圾分类作为资源回收利用的重要环节之一, 可以有效地提高资源回收利用效率, 进一步减轻环境污染带来的危害. 随着现代工业逐步智能化, 传统的图像分类算法已经不能满足垃圾分拣设备的要求. 本文提出一种基于卷积神经网络的垃圾图像分类模型 (Garbage Classification Ne…

javaWeb项目-社区医院管理服务系统功能介绍

项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1a;Vue、ElementUI 关键技术&#xff1a;springboot、SSM、vue、MYSQL、MAVEN 数据库工具&#xff1a;Navicat、SQLyog 1、Java技术 Java语…

微博评论爬取

import requests import csv# 打开CSV文件以写入数据 f open(data.csv, modea, encodingutf-8-sig, newline) csv_writer csv.DictWriter(f, fieldnames[昵称, 性别, 归属地, 内容]) csv_writer.writeheader()# 定义一个函数用于获取评论内容 def GetContent(max_id):# 设置请…

吴恩达机器学习笔记:第 8 周-13 聚类(Clustering)13.3-13.5

目录 第 8 周 13、 聚类(Clustering)13.3 优化目标13.4 随机初始化13.5 选择聚类数 第 8 周 13、 聚类(Clustering) 13.3 优化目标 K-均值最小化问题&#xff0c;是要最小化所有的数据点与其所关联的聚类中心点之间的距离之和&#xff0c;因此 K-均值的代价函数&#xff08;又…

ShardingSphere-JDBC快速入门

ShardingSphere-JDBC读写分离快速入门 一、ShardingSphere-JDBC 读写分离1.创建springboot程序1.1 添加依赖1.2 java代码1.3 配置 2.测试 二、ShardingSphere-JDBC垂直分片1.创建springboot程序1.1 导入依赖1.2 java代码1.3 配置 2.测试 三、ShardingSphere-JDBC水平分片1.创建…

第二证券|三大利好突袭!港股,这次不一样?

密布利好突袭香港。 港股的接连上攻&#xff0c;让商场兴奋不已。行情转好的背后&#xff0c;有三大利好支撑&#xff1a; 一是&#xff0c;香港金融办理局&#xff08;以下简称“香港金管局”&#xff09;正密布投进流动性&#xff0c;4月22日、23日&#xff0c;分别经过贴现…