mpc_python_learn/notebooks/2.4-MPC-kinematics.ipynb

1108 lines
298 KiB
Plaintext
Raw Normal View History

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# 2.4 阿克曼运动学MPC\n",
"本片笔记描述阿克曼底盘的运动学MPC控制算法"
]
},
{
"cell_type": "markdown",
"source": [
"## 阿克曼运动学模型建模\n",
"\n",
"系统的状态变量为:\n",
"* $x$:机器人前向方向为正\n",
"* $y$:机器人左侧方向为正\n",
"* $\\theta$:机器人航向角\n",
"\n",
"系统的控制变量为:\n",
"* $v$:机器人速度\n",
"* $\\delta$:前轮转角\n",
"\n",
"根据两轮自行车模型,可以得到机器人的运动学模型:\n",
"* $\\dot{x} = v \\cos(\\theta)$\n",
"* $\\dot{y} = v \\sin(\\theta)$\n",
"* $\\dot{theta} = \\frac{v \\tan(\\delta)}{L}$\n",
"\n",
"其中,$L$为机器人的轴距\n",
"\n",
"离散化后,可以得到:\n",
"* $x_{t+1} = x_t + v_t \\cos(\\theta_t) \\Delta t$\n",
"* $y_{t+1} = y_t + v_t \\sin(\\theta_t) \\Delta t$\n",
"* $\\theta_{t+1} = \\theta_t + \\frac{v_t \\tan(\\delta_t)}{L} \\Delta t$\n",
"\n",
"这里,我们假设机器人的速度和转角是控制变量,即$v_t$和$\\delta_t$是已知的,我们可以通过上述公式计算出下一个时刻的状态。\n",
"\n",
"上述模型是一个非线性模型我们可以通过线性化的方式来近似这个模型这样就可以使用MPC来进行控制。\n",
"\n",
"使用泰勒级数在参考点$(\\bar{x}, \\bar{u})$处线性化模型(每步都需要重新线性化模型)\n",
"\n",
"$ f(x,u) \\approx f(\\bar{x}, \\bar{u}) + \\frac{\\partial f}{\\partial x} (x - \\bar{x}) + \\frac{\\partial f}{\\partial u} (u - \\bar{u})$\n",
"\n",
"重写为状态空间的形式:\n",
"\n",
"$ f(x,u) \\approx f(\\bar{x}, \\bar{u}) + A|_{x=\\bar{x},u=\\bar{u}}(x - \\bar{x}) + B|_{x=\\bar{x},u=\\bar{u}}(u - \\bar{u})$\n",
"\n",
"其中\n",
"\n",
"$\n",
"A =\n",
"\\quad\n",
"\\begin{bmatrix}\n",
"\\frac{\\partial f(x,u)}{\\partial x} & \\frac{\\partial f(x,u)}{\\partial y} & \\frac{\\partial f(x,u)}{\\partial \\theta} \\\\\n",
"\\end{bmatrix}\n",
"\\quad\n",
"=\n",
"\\displaystyle \\left[\\begin{matrix}0 & 0 & - v \\sin{\\left(\\theta \\right)}\\\\0 & 0 & v \\cos{\\left(\\theta \\right)}\\\\0 & 0 & 0\\end{matrix}\\right]\n",
"$\n",
"\n",
"and\n",
"\n",
"$\n",
"B = \n",
"\\quad\n",
"\\begin{bmatrix}\n",
"\\frac{\\partial f(x,u)}{\\partial v} & \\frac{\\partial f(x,u)}{\\partial \\delta} \\\\\n",
"\\end{bmatrix}\n",
"\\quad\n",
"= \n",
"\\displaystyle \\left[\\begin{matrix}\\cos{\\left(\\theta \\right)} & 0\\\\ \\sin{\\left(\\theta \\right)} & 0\\\\ \\frac{\\tan{\\left(\\delta \\right)}}{L} & \\frac{v \\left(\\tan^{2}{\\left(\\delta \\right)} + 1\\right)}{L}\\end{matrix}\\right]\n",
"$\n",
"\n",
"矩阵A和B是雅可比矩阵\n",
"\n",
"所以,离散化后的模型为:\n",
"\n",
"* $ x_{t+1} = x_t + (f(\\bar{x}, \\bar{u}) + A|_{x=\\bar{x},u=\\bar{u}}(x - \\bar{x}) + B|_{x=\\bar{x},u=\\bar{u}}(u - \\bar{u})) \\Delta t $\n",
"\n",
"* $ x_{t+1} = (I + A\\Delta t)x_t + B\\Delta t u_t + \\Delta t (f(\\bar{x}, \\bar{u}) - A\\bar{x} - B\\bar{u}) $\n",
"\n",
"写为矩阵形式:\n",
"\n",
"* $ x_{t+1} = A'x_t + B'u_t + C' $\n",
"\n",
"* $ A' = I + \\Delta t A|_{x=\\bar{x},u=\\bar{u}} $\n",
"\n",
"* $ B' = \\Delta t B|_{x=\\bar{x},u=\\bar{u}} $\n",
"\n",
"* $ C' = \\Delta t (f(\\bar{x}, \\bar{u}) - A|_{x=\\bar{x},u=\\bar{u}}\\bar{x} - B|_{x=\\bar{x},u=\\bar{u}}\\bar{u}) $\n"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "markdown",
"source": [
"## 常微分方程模型\n",
"用于仿真车辆运动可作为真值来验证MPC控制的效果"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 20,
"outputs": [],
"source": [
"import numpy as np\n",
"from scipy.integrate import odeint\n",
"from scipy.interpolate import interp1d\n",
"import cvxpy as cp\n",
"\n",
"import matplotlib.pyplot as plt\n",
"\n",
"plt.style.use(\"ggplot\")\n",
"\n",
"import time\n",
"\n",
"# 定义常量\n",
"N = 3 # 状态变量 x, y, theta\n",
"M = 2 # 控制变量 v, delta\n",
"T = 40 # 预测视野 \n",
"DT = 0.2 # 离散步长 [s]\n",
"L = 0.3 # 车辆长度 [m]\n",
"\n",
"# 使用连续性方程\n",
"def kinematics_ode_model(x,t,u):\n",
" \"\"\"\n",
" Returns the set of ODE of the vehicle model.\n",
" 返回车辆模型的ODE模型\n",
" \"\"\"\n",
"\n",
"\n",
" dxdt = u[0] * np.cos(x[2])\n",
" dydt = u[0] * np.sin(x[2])\n",
" dthetadt = u[0] * np.tan(u[1]) / L\n",
"\n",
" dqdt = [dxdt, dydt, dthetadt]\n",
"\n",
" return dqdt\n",
"\n",
"\n",
"def predict(x0, u):\n",
" \"\"\" \"\"\"\n",
"\n",
" x_ = np.zeros((N, T + 1))\n",
"\n",
" x_[:, 0] = x0\n",
"\n",
" # solve ODE\n",
" for t in range(1, T + 1):\n",
"\n",
" tspan = [0, DT]\n",
" x_next = odeint(kinematics_ode_model, x0, tspan, args=(u[:, t - 1],))\n",
"\n",
" x0 = x_next[1]\n",
" x_[:, t] = x_next[1]\n",
"\n",
" return x_"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-10-24T03:04:45.623427Z",
"start_time": "2024-10-24T03:04:45.594094Z"
}
}
},
{
"cell_type": "code",
"execution_count": 21,
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 1.82 ms, sys: 15 μs, total: 1.83 ms\n",
"Wall time: 1.84 ms\n"
]
}
],
"source": [
"%%time\n",
"\n",
"u_bar = np.zeros((M, T))\n",
"u_bar[0, :] = 1.0 # m/s\n",
"u_bar[1, :] = np.radians(-np.pi / 4) # rad\n",
"\n",
"x0 = np.zeros(N)\n",
"x0[0] = 0\n",
"x0[1] = 1\n",
"x0[2] = np.radians(0)\n",
"\n",
"x_bar = predict(x0, u_bar)"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-10-24T03:04:46.199286Z",
"start_time": "2024-10-24T03:04:46.187194Z"
}
}
},
{
"cell_type": "code",
"execution_count": 22,
"outputs": [
{
"data": {
"text/plain": "<Figure size 640x480 with 2 Axes>",
"image/png": "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
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# plot trajectory\n",
"plt.subplot(2, 2, 1)\n",
"plt.plot(x_bar[0, :], x_bar[1, :])\n",
"plt.plot(np.linspace(0, 10, T + 1), np.zeros(T + 1), \"b-\")\n",
"plt.axis(\"equal\")\n",
"plt.ylabel(\"y\")\n",
"plt.xlabel(\"x\")\n",
"\n",
"plt.subplot(2, 2, 2)\n",
"plt.plot(np.degrees(x_bar[2, :]))\n",
"plt.ylabel(\"theta(t) [deg]\")\n",
"# plt.xlabel('time')\n",
"\n",
"\n",
"plt.tight_layout()\n",
"plt.show()"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-10-24T03:04:46.816219Z",
"start_time": "2024-10-24T03:04:46.759086Z"
}
}
},
{
"cell_type": "markdown",
"source": [
"## 线性化状态空间模型"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "code",
"execution_count": 23,
"outputs": [],
"source": [
"def get_linear_model(x_bar, u_bar):\n",
" \"\"\"\n",
" Computes the LTI approximated state space model x' = Ax + Bu + C\n",
" \"\"\"\n",
"\n",
" x = x_bar[0]\n",
" y = x_bar[1]\n",
" theta = x_bar[2]\n",
"\n",
" v = u_bar[0]\n",
" delta = u_bar[1]\n",
"\n",
" A = np.zeros((N, N))\n",
" A[0, 2] = -v * np.sin(theta)\n",
" A[1, 2] = v * np.cos(theta)\n",
" A_lin = np.eye(N) + DT * A\n",
"\n",
" B = np.zeros((N, M))\n",
" B[0, 0] = np.cos(theta)\n",
" B[1, 0] = np.sin(theta)\n",
" B[2, 0] = v * np.tan(delta) / L\n",
" B[2, 1] = v / (L * np.cos(delta) ** 2)\n",
" B_lin = DT * B\n",
"\n",
" f_xu = np.array(\n",
" [v * np.cos(theta), v * np.sin(theta), v * np.tan(delta) / L]\n",
" ).reshape(N, 1)\n",
" C_lin = DT * (\n",
" f_xu - np.dot(A, x_bar.reshape(N, 1)) - np.dot(B, u_bar.reshape(M, 1))\n",
" )\n",
"\n",
" return np.round(A_lin, 4), np.round(B_lin, 4), np.round(C_lin, 4)"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-10-24T03:04:48.033205Z",
"start_time": "2024-10-24T03:04:48.027595Z"
}
}
},
{
"cell_type": "code",
"execution_count": 24,
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"CPU times: user 2.04 ms, sys: 1.79 ms, total: 3.83 ms\n",
"Wall time: 2.54 ms\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/var/folders/hd/8kg_jtmd6svgg_sc384pbcdm0000gn/T/ipykernel_9821/761030008.py:14: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n",
" A[0, 2] = -v * np.sin(theta)\n",
"/var/folders/hd/8kg_jtmd6svgg_sc384pbcdm0000gn/T/ipykernel_9821/761030008.py:15: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n",
" A[1, 2] = v * np.cos(theta)\n",
"/var/folders/hd/8kg_jtmd6svgg_sc384pbcdm0000gn/T/ipykernel_9821/761030008.py:19: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n",
" B[0, 0] = np.cos(theta)\n",
"/var/folders/hd/8kg_jtmd6svgg_sc384pbcdm0000gn/T/ipykernel_9821/761030008.py:20: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n",
" B[1, 0] = np.sin(theta)\n",
"/var/folders/hd/8kg_jtmd6svgg_sc384pbcdm0000gn/T/ipykernel_9821/761030008.py:21: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n",
" B[2, 0] = v * np.tan(delta) / L\n",
"/var/folders/hd/8kg_jtmd6svgg_sc384pbcdm0000gn/T/ipykernel_9821/761030008.py:22: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n",
" B[2, 1] = v / (L * np.cos(delta) ** 2)\n"
]
}
],
"source": [
"%%time\n",
"\n",
"u_bar = np.zeros((M, T))\n",
"u_bar[0, :] = 1.0 # m/s\n",
"u_bar[1, :] = np.radians(-np.pi / 4) # rad\n",
"\n",
"x0 = np.zeros(N)\n",
"x0[0] = 0\n",
"x0[1] = 1\n",
"x0[2] = np.radians(0)\n",
"\n",
"x_bar = np.zeros((N, T + 1))\n",
"x_bar[:, 0] = x0\n",
"\n",
"for t in range(1, T + 1):\n",
" xt = x_bar[:, t - 1].reshape(N, 1)\n",
" ut = u_bar[:, t - 1].reshape(M, 1)\n",
"\n",
" A, B, C = get_linear_model(xt, ut)\n",
"\n",
" xt_plus_one = np.dot(A, xt) + np.dot(B, ut) + C\n",
"\n",
" x_bar[:, t] = np.squeeze(xt_plus_one)"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-10-24T03:04:48.720648Z",
"start_time": "2024-10-24T03:04:48.703990Z"
}
}
},
{
"cell_type": "code",
"execution_count": 25,
"outputs": [
{
"data": {
"text/plain": "<Figure size 640x480 with 2 Axes>",
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAnUAAAEDCAYAAABTZPIVAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAABDBklEQVR4nO3deXgUVdbH8e+tLBAISdgDJEACBEVWdwVfUBTRwQUXREcZVAaV4LiACMRRcAQERMYRVBQUdERFlnEXRIVRXFBRVhURwh5IDCHsJKn7/tESzSSBgEkqXfl9nqcf0lW3us+hOtUnt6ruNdZai4iIiIgENcfrAERERETkj1NRJyIiIuIDKupEREREfEBFnYiIiIgPqKgTERER8QEVdSIiIiI+oKJORERExAdU1ImIiIj4gIo6ERERER8I9ToAr+zatYvc3Nxjtqtbty7p6enlEFH58mte4N/c/JoXlCy30NBQatasWU4R+VdlP/aBf3Pza17g39xKmldJj3+VtqjLzc0lJyfnqG2MMflt/TSbml/zAv/m5te8wN+5VUSV+dgH/s3Nr3mBf3Mri7x0+lVERETEB1TUiYiIiPhApT39KiISjObPn8+bb75JVlYWcXFx9O3bl5NPPtnrsESkAlBPnYhIkPjss8+YPn06V111FWPHjuXkk09m9OjRZGRkeB2aiFQAKupERILE22+/zQUXXEDXrl3ze+nq1KnDggULSvV9rOviHthfqq8pImVPp19FRIJAbm4u69ev58orryywvG3btvz444+F2ufk5BS4y9UYQ0RERP7PR+O+OZMdy5fC7UMx9Rr88eArkCO5H+v/INj4NS/wb25lkZeKOhGRIJCdnY3rukRHRxdYHh0dTVZWVqH28+bNY/bs2fnPExISGDt2LHXr1j3q+7j795L2+cfkZuzAjBpEnaFjqHrq2aWSQ0USGxvrdQhlwq95gX9zK828VNSJiASRov6qL2pZz5496dGjR6E26enpxxx82AwbT/hz4zn8w0rSH/wbzrU3Yy66whc9JcYYYmNjSUtL892YZ37MC/yb2/HkFRoaesw/yEBFnYhIUIiKisJxnEK9crt37y7UewcQFhZGWFhYka91rC8QE12Teo9OYev4B7FLFuLOmobZvAFz0wBMWPgJ51CRWGt9VSAc4de8wL+5lWZeulFCRCQIhIaGkpiYyIoV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
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# plot trajectory\n",
"plt.subplot(2, 2, 1)\n",
"plt.plot(x_bar[0, :], x_bar[1, :])\n",
"plt.plot(np.linspace(0, 10, T + 1), np.zeros(T + 1), \"b-\")\n",
"plt.axis(\"equal\")\n",
"plt.ylabel(\"y\")\n",
"plt.xlabel(\"x\")\n",
"\n",
"plt.subplot(2, 2, 2)\n",
"plt.plot(np.degrees(x_bar[2, :]))\n",
"plt.ylabel(\"theta(t)\")\n",
"# plt.xlabel('time')\n",
"\n",
"\n",
"plt.tight_layout()\n",
"plt.show()"
],
"metadata": {
"collapsed": false,
"ExecuteTime": {
"end_time": "2024-10-24T03:04:49.296366Z",
"start_time": "2024-10-24T03:04:49.239223Z"
}
}
},
{
"cell_type": "markdown",
"source": [
"对比两个轨迹,可以看到线性化的模型能够很好的近似真实的模型"
],
"metadata": {
"collapsed": false
}
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Linear MPC Formulation 线性MPC表述\n",
"\n",
"Linear MPC makes use of the **LTI** (Linear time invariant) discrete state space model, wich represents a motion model used for Prediction.\n",
"线性MPC使用**LTI**(线性时不变)离散状态空间模型,该模型表示用于预测的运动模型。\n",
"\n",
"$x_{t+1} = Ax_t + Bu_t$\n",
"\n",
"The LTI formulation means that **future states** are linearly related to the current state and actuator signal. Hence, the MPC seeks to find a **control policy** U over a finite lenght horizon.\n",
"LTI表述意味着**未来状态**与当前状态和执行器信号之间是线性相关的。因此MPC试图找到一个有限长度视野上的**控制策略**U。\n",
"\n",
"$U={u_{t|t}, u_{t+1|t}, ...,u_{t+T|t}}$\n",
"\n",
"The objective function used minimize (drive the state to 0) is:\n",
"用于最小化将状态驱动至0的目标函数为\n",
"\n",
"$\n",
"\\begin{equation}\n",
"\\begin{aligned}\n",
"\\min_{} \\quad & \\sum^{t+T-1}_{j=t} x^T_{j|t}Qx_{j|t} + u^T_{j|t}Ru_{j|t}\\\\\n",
"\\textrm{s.t.} \\quad & x(0) = x0\\\\\n",
" & x_{j+1|t} = Ax_{j|t}+Bu_{j|t}) \\quad \\textrm{for} \\quad t<j<t+T-1 \\\\\n",
"\\end{aligned}\n",
"\\end{equation}\n",
"$\n",
"\n",
"Other linear constrains may be applied,for instance on the control variable:\n",
"其他线性约束也可以应用,比如对控制变量的约束\n",
"\n",
"$ U_{MIN} < u_{j|t} < U_{MAX} \\quad \\textrm{for} \\quad t<j<t+T-1 $\n",
"\n",
"The objective fuction accounts for quadratic error on deviation from 0 of the state and the control inputs sequences. Q and R are the **weight matrices** and are used to tune the response.\n",
"目标函数考虑了状态偏离0的二次误差以及控制输入序列的二次误差。矩阵Q和R是权重矩阵用于调整系统响应\n",
"Because the goal is tracking a **reference signal** such as a trajectory, the objective function is rewritten as:\n",
"由于目标是跟踪参考信号(例如轨迹),目标函数被重写为:\n",
"\n",
"$\n",
"\\begin{equation}\n",
"\\begin{aligned}\n",
"\\min_{} \\quad & \\sum^{t+T-1}_{j=t} \\delta x^T_{j|t}Q\\delta x_{j|t} + u^T_{j|t}Ru_{j|t}\n",
"\\end{aligned}\n",
"\\end{equation}\n",
"$\n",
"\n",
"where the error w.r.t desired state is accounted for:\n",
"其中考虑了相对于期望状态的误差:\n",
"\n",
"$ \\delta x = x_{j,t,ref} - x_{j,t} $"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Problem Formulation: Study case 研究案例问题表述\n",
"\n",
"In this case, the objective function to minimize is given by:\n",
"在这种情况下,要最小化的目标函数为:\n",
"\n",
"https://borrelli.me.berkeley.edu/pdfpub/IV_KinematicMPC_jason.pdf\n",
"\n",
"$\n",
"\\begin{equation}\n",
"\\begin{aligned}\n",
"\\min_{} \\quad & \\sum^{t+T-1}_{j=t} (x_{j} - x_{j,ref})^TQ(x_{j} - x_{j,ref}) + \\sum^{t+T-1}_{j=t+1} u^T_{j}Ru_{j} + (u_{j} - u_{j-1})^TP(u_{j} - u_{j-1}) \\\\\n",
"\\textrm{s.t.} \\quad & x(0) = x0\\\\\n",
" & x_{j+1} = Ax_{j}+Bu_{j} \\quad \\textrm{for} \\quad t<j<t+T-1 \\\\\n",
" & u_{MIN} < u_{j} < u_{MAX} \\quad \\textrm{for} \\quad t<j<t+T-1 \\\\\n",
" & \\dot{u}_{MIN} < \\frac{(u_{j} - u_{j-1})}{ts} < \\dot{u}_{MAX} \\quad \\textrm{for} \\quad t+1<j<t+T-1 \\\\\n",
"\\end{aligned}\n",
"\\end{equation}\n",
"$\n",
"\n",
"\n",
"Where R,P,Q are the cost matrices used to tune the response.\n",
"R,P,Q是用于调整响应的成本矩阵。\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## PRELIMINARIES 准备工作\n",
"\n",
"* linearized system dynamics 线性化系统动力学\n",
"* function to represent the track 轨迹函数\n",
"* function to represent the **reference trajectory** to track 跟踪的**参考轨迹**函数"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {
"ExecuteTime": {
"end_time": "2024-10-24T03:05:25.387867Z",
"start_time": "2024-10-24T03:05:25.379154Z"
}
},
"outputs": [],
"source": [
"\"\"\"\n",
"Control problem statement.\n",
"控制问题陈述。\n",
"\"\"\"\n",
"\n",
"def compute_path_from_wp(start_xp, start_yp, step=0.1):\n",
" \"\"\"\n",
" Computes a reference path given a set of waypoints\n",
" 给定一组路径点,计算参考路径\n",
" \"\"\"\n",
"\n",
" final_xp = []\n",
" final_yp = []\n",
" delta = step # [m]\n",
"\n",
" for idx in range(len(start_xp) - 1):\n",
" section_len = np.sum(\n",
" np.sqrt(\n",
" np.power(np.diff(start_xp[idx : idx + 2]), 2)\n",
" + np.power(np.diff(start_yp[idx : idx + 2]), 2)\n",
" )\n",
" )\n",
"\n",
" interp_range = np.linspace(0, 1, np.floor(section_len / delta).astype(int))\n",
"\n",
" fx = interp1d(np.linspace(0, 1, 2), start_xp[idx : idx + 2], kind=1)\n",
" fy = interp1d(np.linspace(0, 1, 2), start_yp[idx : idx + 2], kind=1)\n",
"\n",
" final_xp = np.append(final_xp, fx(interp_range))\n",
" final_yp = np.append(final_yp, fy(interp_range))\n",
"\n",
" dx = np.append(0, np.diff(final_xp))\n",
" dy = np.append(0, np.diff(final_yp))\n",
" theta = np.arctan2(dy, dx)\n",
"\n",
" return np.vstack((final_xp, final_yp, theta))\n",
"\n",
"\n",
"def get_nn_idx(state, path):\n",
" \"\"\"\n",
" Computes the index of the waypoint closest to vehicle\n",
" 计算最接近车辆的路径点的索引\n",
" \"\"\"\n",
"\n",
" dx = state[0] - path[0, :]\n",
" dy = state[1] - path[1, :]\n",
" dist = np.hypot(dx, dy)\n",
" nn_idx = np.argmin(dist)\n",
"\n",
" try:\n",
" v = [\n",
" path[0, nn_idx + 1] - path[0, nn_idx],\n",
" path[1, nn_idx + 1] - path[1, nn_idx],\n",
" ]\n",
" v /= np.linalg.norm(v)\n",
"\n",
" d = [path[0, nn_idx] - state[0], path[1, nn_idx] - state[1]]\n",
"\n",
" if np.dot(d, v) > 0:\n",
" target_idx = nn_idx\n",
" else:\n",
" target_idx = nn_idx + 1\n",
"\n",
" except IndexError as e:\n",
" target_idx = nn_idx\n",
"\n",
" return target_idx\n",
"\n",
"\n",
"def get_ref_trajectory(state, path, target_v):\n",
" \"\"\"\n",
" Adapted from pythonrobotics\n",
" 获取参考轨迹\n",
" 从当前位置开始截取路径上的一段作为参考轨迹其中v=固定的参考的方向转角为0\n",
" \"\"\"\n",
" xref = np.zeros((N, T + 1))\n",
" dref = np.zeros((M, T + 1))\n",
"\n",
" # sp = np.ones((1,T +1))*target_v #speed profile\n",
"\n",
" ncourse = path.shape[1]\n",
"\n",
" ind = get_nn_idx(state, path)\n",
"\n",
" xref[0, 0] = path[0, ind] # X\n",
" xref[1, 0] = path[1, ind] # Y\n",
" xref[2, 0] = path[2, ind] # Theta\n",
" dref[0, 0] = 0.0 # steer operational point should be 0\n",
" dref[1, 0] = 0.0 # speed\n",
"\n",
" dl = 0.05 # Waypoints spacing [m]\n",
" travel = 0.0\n",
"\n",
" for i in range(T + 1):\n",
" travel += abs(target_v) * DT # current V or target V?\n",
" dind = int(round(travel / dl))\n",
"\n",
" if (ind + dind) < ncourse:\n",
" xref[0, i] = path[0, ind + dind]\n",
" xref[1, i] = path[1, ind + dind]\n",
" xref[2, i] = path[2, ind + dind]\n",
" dref[0, i] = 0.0\n",
" dref[1, i] = target_v\n",
" else:\n",
" xref[0, i] = path[0, ncourse - 1]\n",
" xref[1, i] = path[1, ncourse - 1]\n",
" xref[2, i] = path[2, ncourse - 1]\n",
" dref[0, i] = 0.0\n",
" dref[1, i] = 0.0\n",
"\n",
" return xref, dref"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## MPC \n",
"\n",
"test single iteration\n",
"测试单次迭代"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {
"ExecuteTime": {
"end_time": "2024-10-24T03:05:26.555645Z",
"start_time": "2024-10-24T03:05:26.481067Z"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"===============================================================================\n",
" CVXPY \n",
" v1.5.3 \n",
"===============================================================================\n",
"(CVXPY) Oct 24 11:05:26 AM: Your problem has 203 variables, 243 constraints, and 0 parameters.\n",
"(CVXPY) Oct 24 11:05:26 AM: It is compliant with the following grammars: DCP, DQCP\n",
"(CVXPY) Oct 24 11:05:26 AM: (If you need to solve this problem multiple times, but with different data, consider using parameters.)\n",
"(CVXPY) Oct 24 11:05:26 AM: CVXPY will first compile your problem; then, it will invoke a numerical solver to obtain a solution.\n",
"(CVXPY) Oct 24 11:05:26 AM: Your problem is compiled with the CPP canonicalization backend.\n",
"-------------------------------------------------------------------------------\n",
" Compilation \n",
"-------------------------------------------------------------------------------\n",
"(CVXPY) Oct 24 11:05:26 AM: Compiling problem (target solver=OSQP).\n",
"(CVXPY) Oct 24 11:05:26 AM: Reduction chain: CvxAttr2Constr -> Qp2SymbolicQp -> QpMatrixStuffing -> OSQP\n",
"(CVXPY) Oct 24 11:05:26 AM: Applying reduction CvxAttr2Constr\n",
"(CVXPY) Oct 24 11:05:26 AM: Applying reduction Qp2SymbolicQp\n",
"(CVXPY) Oct 24 11:05:26 AM: Applying reduction QpMatrixStuffing\n",
"(CVXPY) Oct 24 11:05:26 AM: Applying reduction OSQP\n",
"(CVXPY) Oct 24 11:05:26 AM: Finished problem compilation (took 5.022e-02 seconds).\n",
"-------------------------------------------------------------------------------\n",
" Numerical solver \n",
"-------------------------------------------------------------------------------\n",
"(CVXPY) Oct 24 11:05:26 AM: Invoking solver OSQP to obtain a solution.\n",
"-------------------------------------------------------------------------------\n",
" Summary \n",
"-------------------------------------------------------------------------------\n",
"(CVXPY) Oct 24 11:05:26 AM: Problem status: optimal\n",
"(CVXPY) Oct 24 11:05:26 AM: Optimal value: 4.687e+02\n",
"(CVXPY) Oct 24 11:05:26 AM: Compilation took 5.022e-02 seconds\n",
"(CVXPY) Oct 24 11:05:26 AM: Solver (including time spent in interface) took 3.240e-03 seconds\n",
"CPU times: user 77.8 ms, sys: 7.17 ms, total: 85 ms\n",
"Wall time: 87.9 ms\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/var/folders/hd/8kg_jtmd6svgg_sc384pbcdm0000gn/T/ipykernel_9821/761030008.py:14: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n",
" A[0, 2] = -v * np.sin(theta)\n",
"/var/folders/hd/8kg_jtmd6svgg_sc384pbcdm0000gn/T/ipykernel_9821/761030008.py:15: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n",
" A[1, 2] = v * np.cos(theta)\n",
"/var/folders/hd/8kg_jtmd6svgg_sc384pbcdm0000gn/T/ipykernel_9821/761030008.py:19: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n",
" B[0, 0] = np.cos(theta)\n",
"/var/folders/hd/8kg_jtmd6svgg_sc384pbcdm0000gn/T/ipykernel_9821/761030008.py:20: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n",
" B[1, 0] = np.sin(theta)\n",
"/var/folders/hd/8kg_jtmd6svgg_sc384pbcdm0000gn/T/ipykernel_9821/761030008.py:21: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n",
" B[2, 0] = v * np.tan(delta) / L\n",
"/var/folders/hd/8kg_jtmd6svgg_sc384pbcdm0000gn/T/ipykernel_9821/761030008.py:22: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n",
" B[2, 1] = v / (L * np.cos(delta) ** 2)\n"
]
}
],
"source": [
"%%time\n",
"\n",
"# 限制条件\n",
"MAX_SPEED = 1.5 # m/s\n",
"MAX_STEER = np.radians(30) # rad\n",
"REF_VEL = 1.0 # 目标路径参考速度\n",
"\n",
"#获取参考轨迹,线性插值,三个点[0,0],[3,0],[6,0]\n",
"track = compute_path_from_wp([0, 3, 6], [0, 0, 0], 0.05)\n",
"\n",
"# Starting Condition 初始条件\n",
"x0 = np.zeros(N)\n",
"x0[0] = 0 # x\n",
"x0[1] = -0.25 # y\n",
"x0[2] = np.radians(-0) # yaw\n",
"\n",
"# starting guess 开始猜测\n",
"u_bar = np.zeros((M, T))\n",
"u_bar[0, :] = REF_VEL # v\n",
"u_bar[1, :] = 0.1 # delta\n",
"\n",
"# dynamics starting state w.r.t world frame 与世界坐标系相关的动力学起始状态\n",
"x_bar = np.zeros((N, T + 1)) # 4x21\n",
"x_bar[:, 0] = x0\n",
"\n",
"# prediction for linearization of costrains 用于约束线性化的预测\n",
"# 这部分应用线性模型,得到预测的轨迹\n",
"for t in range(1, T + 1):\n",
" xt = x_bar[:, t - 1].reshape(N, 1)\n",
" ut = u_bar[:, t - 1].reshape(M, 1)\n",
" A, B, C = get_linear_model(xt, ut) # 获取在t - 1时刻的线性近似模型\n",
" xt_plus_one = np.squeeze(np.dot(A, xt) + np.dot(B, ut) + C)\n",
" x_bar[:, t] = xt_plus_one # 获取t时刻的状态\n",
"\n",
"# x_bar是根据猜测的u_bar获取的预测状态估计\n",
"\n",
"# CVXPY Linear MPC problem statement CVXPY线性MPC问题陈述\n",
"x = cp.Variable((N, T + 1)) # 4x21维状态向量\n",
"u = cp.Variable((M, T)) # 2x20维控制向量\n",
"cost = 0\n",
"constr = []\n",
"\n",
"# Cost Matrices\n",
"Q = np.diag([10, 10, 10]) # state error cost 状态误差成本\n",
"Qf = np.diag([10, 10, 10]) # state final error cost 最终状态误差成本\n",
"R = np.diag([10, 10]) # input cost 输入成本\n",
"R_ = np.diag([10, 10]) # input rate of change cost 输入变化率成本\n",
"\n",
"# Get Reference_traj 获取参考轨迹,根据当前位置截取的路径上的一系列点,并赋值目标速度和转角\n",
"# x_ref 表示参考状态d_ref表示参考转角\n",
"x_ref, d_ref = get_ref_trajectory(x_bar[:, 0], track, REF_VEL)\n",
"\n",
"# Prediction Horizon 预测视野\n",
"for t in range(T):\n",
"\n",
" # Tracking Error 跟踪误差\n",
" cost += cp.quad_form(x[:, t] - x_ref[:, t], Q)\n",
"\n",
" # Actuation effort 执行努力\n",
" cost += cp.quad_form(u[:, t], R)\n",
"\n",
" # Actuation rate of change 变化率\n",
" if t < (T - 1):\n",
" cost += cp.quad_form(u[:, t + 1] - u[:, t], R_)\n",
"\n",
" # Kinrmatics Constrains (Linearized model) 运动学约束(线性化模型)\n",
" A, B, C = get_linear_model(x_bar[:, t], u_bar[:, t])\n",
" constr += [x[:, t + 1] == A @ x[:, t] + B @ u[:, t] + C.flatten()]\n",
"\n",
"# Final Point tracking 最终点跟踪\n",
"cost += cp.quad_form(x[:, T] - x_ref[:, T], Qf)\n",
"\n",
"# sums problem objectives and concatenates constraints. 求和问题目标并连接约束。\n",
"constr += [x[:, 0] == x_bar[:, 0]] # starting condition 初始条件\n",
"constr += [u[0, :] <= MAX_SPEED] # max speed 最大速度\n",
"constr += [u[0, :] >= 0.0] # min_speed (not really needed) 最小速度(实际上不需要)\n",
"constr += [cp.abs(u[1, :]) <= MAX_STEER] # max steer 最大转向\n",
"# for t in range(T):\n",
"# if t < (T - 1):\n",
"# constr += [cp.abs(u[0,t] - u[0,t-1])/DT <= MAX_ACC] #max acc\n",
"# constr += [cp.abs(u[1,t] - u[1,t-1])/DT <= MAX_STEER] #max steer\n",
"\n",
"prob = cp.Problem(cp.Minimize(cost), constr) # 构建问题\n",
"solution = prob.solve(solver=cp.OSQP, verbose=True) # 求解"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {
"ExecuteTime": {
"end_time": "2024-10-24T03:05:27.460730Z",
"start_time": "2024-10-24T03:05:27.349153Z"
}
},
"outputs": [
{
"data": {
"text/plain": "<Figure size 640x480 with 4 Axes>",
"image/png": "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
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"x_mpc = np.array(x.value[0, :]).flatten()\n",
"y_mpc = np.array(x.value[1, :]).flatten()\n",
"theta_mpc = np.array(x.value[2, :]).flatten()\n",
"v_mpc = np.array(u.value[0, :]).flatten()\n",
"delta_mpc = np.array(u.value[1, :]).flatten()\n",
"\n",
"# simulate robot state trajectory for optimized U\n",
"x_traj = predict(x0, np.vstack((v_mpc, delta_mpc)))\n",
"\n",
"# plt.figure(figsize=(15,10))\n",
"# plot trajectory\n",
"plt.subplot(2, 2, 1)\n",
"plt.plot(track[0, :], track[1, :], \"b\")\n",
"plt.plot(x_ref[0, :], x_ref[1, :], \"g+\")\n",
"plt.plot(x_traj[0, :], x_traj[1, :]) #根据mpc优化后的a和delta预测的轨迹\n",
"plt.axis(\"equal\")\n",
"plt.ylabel(\"y\")\n",
"plt.xlabel(\"x\")\n",
"\n",
"# plot v(t)\n",
"plt.subplot(2, 2, 3)\n",
"plt.plot(v_mpc)\n",
"plt.ylabel(\"v_in(t)\")\n",
"# plt.xlabel('time')\n",
"\n",
"\n",
"plt.subplot(2, 2, 2)\n",
"plt.plot(theta_mpc) \n",
"plt.ylabel(\"theta(t)\") # 航向角\n",
"\n",
"plt.subplot(2, 2, 4)\n",
"plt.plot(delta_mpc)\n",
"plt.ylabel(\"d_in(t)\") # 前轮转角\n",
"\n",
"plt.tight_layout()\n",
"plt.show()\n",
"\n",
"# 下图展示的结果并不准确\n",
"# 这是因为在做约束条件中xt+1 = Axt + But + C时ABC是线性化的模型且是基于猜测的x_bar和u_bar来线性化的\n",
"# 所以需要通过滚动优化,获得更准确的猜测和状态猜测\n",
"# 这里有一个问题那是否要将这次计算的u作为下一次的猜测基础"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## full track demo "
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {
"ExecuteTime": {
"end_time": "2024-10-24T03:11:52.521780Z",
"start_time": "2024-10-24T03:11:26.227974Z"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/var/folders/hd/8kg_jtmd6svgg_sc384pbcdm0000gn/T/ipykernel_9821/761030008.py:14: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n",
" A[0, 2] = -v * np.sin(theta)\n",
"/var/folders/hd/8kg_jtmd6svgg_sc384pbcdm0000gn/T/ipykernel_9821/761030008.py:15: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n",
" A[1, 2] = v * np.cos(theta)\n",
"/var/folders/hd/8kg_jtmd6svgg_sc384pbcdm0000gn/T/ipykernel_9821/761030008.py:19: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n",
" B[0, 0] = np.cos(theta)\n",
"/var/folders/hd/8kg_jtmd6svgg_sc384pbcdm0000gn/T/ipykernel_9821/761030008.py:20: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n",
" B[1, 0] = np.sin(theta)\n",
"/var/folders/hd/8kg_jtmd6svgg_sc384pbcdm0000gn/T/ipykernel_9821/761030008.py:21: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n",
" B[2, 0] = v * np.tan(delta) / L\n",
"/var/folders/hd/8kg_jtmd6svgg_sc384pbcdm0000gn/T/ipykernel_9821/761030008.py:22: DeprecationWarning: Conversion of an array with ndim > 0 to a scalar is deprecated, and will error in future. Ensure you extract a single element from your array before performing this operation. (Deprecated NumPy 1.25.)\n",
" B[2, 1] = v / (L * np.cos(delta) ** 2)\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"CVXPY Optimization Time: Avrg: 0.1320s Max: 0.1657s Min: 0.1066s\n"
]
}
],
"source": [
"track = compute_path_from_wp(\n",
" [0, 3, 4, 6, 10, 12, 14, 6, 1, 0], [0, 0, 2, 4, 3, 3, -2, -6, -2, -2], 0.05\n",
")\n",
"\n",
"# track = compute_path_from_wp([0,10,10,0],\n",
"# [0,0,1,1],0.05)\n",
"\n",
"sim_duration = 200 # time steps\n",
"opt_time = []\n",
"\n",
"x_sim = np.zeros((N, sim_duration))\n",
"x_sim[0,0] = 0 # x\n",
"x_sim[1,0] = -0.25 # y\n",
"x_sim[2,0] = np.radians(0.0) # yaw\n",
"u_sim = np.zeros((M, sim_duration - 1))\n",
"\n",
"MAX_SPEED = 1.5 # m/s\n",
"MAX_D_ACC = 0.5 # m/s^2\n",
"MAX_STEER = np.radians(30) # rad\n",
"MAX_D_STEER = np.radians(30) # rad/s\n",
"\n",
"REF_VEL = 1.0 # m/s\n",
"\n",
"# Starting Condition\n",
"x0 = np.zeros(N)\n",
"x0 = x_sim[:, 0]\n",
"\n",
"# starting guess\n",
"u_bar = np.zeros((M, T))\n",
"u_bar[0, :] = 1.0 # v\n",
"u_bar[1, :] = 0.0 # delta\n",
"\n",
"for sim_time in range(sim_duration - 1):\n",
"\n",
" iter_start = time.time()\n",
"\n",
" # dynamics starting state\n",
" # 获取当前时刻的状态x_sim是通过ode计算出的真值\n",
" x_bar = np.zeros((N, T + 1))\n",
" x_bar[:, 0] = x_sim[:, sim_time]\n",
"\n",
" # prediction for linearization of costrains\n",
" # 获取各参考点处的线性化模型参数\n",
" for t in range(1, T + 1):\n",
" xt = x_bar[:, t - 1].reshape(N, 1)\n",
" ut = u_bar[:, t - 1].reshape(M, 1)\n",
" A, B, C = get_linear_model(xt, ut)\n",
" xt_plus_one = np.squeeze(np.dot(A, xt) + np.dot(B, ut) + C)\n",
" x_bar[:, t] = xt_plus_one\n",
"\n",
" # CVXPY Linear MPC problem statement\n",
" # 构建MPC问题和求解器\n",
" x = cp.Variable((N, T + 1))\n",
" u = cp.Variable((M, T))\n",
" cost = 0\n",
" constr = []\n",
"\n",
" # Cost Matrices\n",
" Q = np.diag([20, 20, 0]) # state error cost\n",
" Qf = np.diag([30, 30, 0]) # state final error cost\n",
" R = np.diag([10, 10]) # input cost\n",
" R_ = np.diag([30, 10]) # input rate of change cost\n",
"\n",
" # Get Reference_traj\n",
" x_ref, d_ref = get_ref_trajectory(x_bar[:, 0], track, REF_VEL)\n",
"\n",
" # Prediction Horizon\n",
" for t in range(T):\n",
"\n",
" # Tracking Error\n",
" cost += cp.quad_form(x[:, t] - x_ref[:, t], Q)\n",
"\n",
" # Actuation effort\n",
" cost += cp.quad_form(u[:, t], R)\n",
"\n",
" # Actuation rate of change\n",
" if t < (T - 1):\n",
" cost += cp.quad_form(u[:, t + 1] - u[:, t], R_)\n",
" constr += [\n",
" cp.abs(u[0, t + 1] - u[0, t]) / DT <= MAX_D_ACC\n",
" ] # max acc rate of change\n",
" constr += [\n",
" cp.abs(u[1, t + 1] - u[1, t]) / DT <= MAX_D_STEER\n",
" ] # max steer rate of change\n",
"\n",
" # Kinrmatics Constrains (Linearized model)\n",
" A, B, C = get_linear_model(x_bar[:, t], u_bar[:, t])\n",
" constr += [x[:, t + 1] == A @ x[:, t] + B @ u[:, t] + C.flatten()]\n",
"\n",
" # Final Point tracking\n",
" cost += cp.quad_form(x[:, T] - x_ref[:, T], Qf)\n",
"\n",
" # sums problem objectives and concatenates constraints.\n",
" constr += [x[:, 0] == x_bar[:, 0]] # starting condition\n",
" constr += [u[0, :] <= MAX_SPEED] # max speed\n",
" constr += [u[0, :] >= 0.0] # min_speed (not really needed)\n",
"\n",
" # Solve\n",
" prob = cp.Problem(cp.Minimize(cost), constr)\n",
" solution = prob.solve(solver=cp.OSQP, verbose=False)\n",
"\n",
" # retrieved optimized U and assign to u_bar to linearize in next step\n",
" # 将本次计算出的u_bar作为下次猜测的起点\n",
" u_bar = np.vstack(\n",
" (np.array(u.value[0, :]).flatten(), (np.array(u.value[1, :]).flatten()))\n",
" )\n",
" \n",
" # 本次的执行值\n",
" u_sim[:, sim_time] = u_bar[:, 0]\n",
"\n",
" # Measure elpased time to get results from cvxpy\n",
" opt_time.append(time.time() - iter_start)\n",
"\n",
" # 用ode模型仿真车辆运动\n",
" # move simulation to t+1\n",
" tspan = [0, DT]\n",
" x_sim[:, sim_time + 1] = odeint(\n",
" kinematics_ode_model, x_sim[:, sim_time], tspan, args=(u_bar[:, 0],)\n",
" )[1]\n",
"\n",
"print(\n",
" \"CVXPY Optimization Time: Avrg: {:.4f}s Max: {:.4f}s Min: {:.4f}s\".format(\n",
" np.mean(opt_time), np.max(opt_time), np.min(opt_time)\n",
" )\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {
"ExecuteTime": {
"end_time": "2024-10-24T03:14:58.916991Z",
"start_time": "2024-10-24T03:14:58.730488Z"
}
},
"outputs": [
{
"data": {
"text/plain": "<Figure size 1500x1000 with 4 Axes>",
"image/png": "iVBORw0KGgoAAAANSUhEUgAABdEAAAPeCAYAAADj01PlAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdeXhU5f3+8fczmez7vsiWSALIoqgguCJW1GirtFqrUMWlrYoUi6K11rq1tkhFKbWLv9oqBi0Vi6LGBVRsccGtski+7MiajewbJJnz++NkJplshCxMEu7XdeWaOWfOPOc5yYjJPZ/5PMayLAsREREREREREREREWnB4esJiIiIiIiIiIiIiIj0VgrRRURERERERERERETaoBBdRERERERERERERKQNCtFFRERERERERERERNqgEF1EREREREREREREpA0K0UVERERERERERERE2qAQXURERERERERERESkDQrRRURERERERERERETaoBBdRERERERERERERKQNCtFFRERERERERERERNrg9PUEfKW4uJi6ujpfT6NL4uPjKSgo8PU0pA/Ta0i6Sq8h6Sq9hqSr9BqSrtJrSLpKryHpiv7y+nE6nURHR/t6Gn1OR7Kp/vIaAV1Lb3W8XEtX/506bkP0uro6amtrfT2NTjPGAPZ1WJbl49lIX6TXkHSVXkPSVXoNSVfpNSRdpdeQdJVeQ9IVev3IkbKp/vQa0bX0TrqWjlM7FxERERERERERERGRNihEFxERERERERERERFpg0J0EREREREREREREZE2KEQXEREREREREREREWmDQnQRERERERERERERkTYoRBcREREREREREeknrNrD3T6m65//D9eSv3T7uCJ9hUJ0ERERERERERGRfsDasRnXT3+A69Ul3Tfm4UNY776GtTobq6ig28ZtcR5XPdbenVguV4+do7ewyopxrVqBVVnh66lIBylEFxERERERERER6Qdcry+Fujqs15di1VR3TyDtqm+8X1XZ9fHaYC17FtdDs7HeXNb9YxfkYlVXdfu4neX663yspX/D9bff+3oq0kEK0UVERERERERERPoBEx3nue+afQ3Wv57p+qD1TYL4HgyirZWv2revZHXvuHn7cf3ix7jm3tCt43bJlo327cYvfTsP6TCF6CIiIiIiIiIiIv1BXELjfZfLbsNSXta1MZtW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
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# plot trajectory\n",
"grid = plt.GridSpec(4, 5)\n",
"\n",
"plt.figure(figsize=(15, 10))\n",
"\n",
"plt.subplot(grid[0:4, 0:4])\n",
"plt.plot(track[0, :], track[1, :], \"b+\")\n",
"plt.plot(x_sim[0, :], x_sim[1, :])\n",
"plt.axis(\"equal\")\n",
"plt.ylabel(\"y\")\n",
"plt.xlabel(\"x\")\n",
"\n",
"plt.subplot(grid[0, 4])\n",
"plt.plot(u_sim[0, :])\n",
"plt.ylabel(\"v(t) [m/ss]\")\n",
"\n",
"plt.subplot(grid[1, 4])\n",
"plt.plot(np.degrees(u_sim[1, :]))\n",
"plt.ylabel(\"delta(t) [rad]\")\n",
"\n",
"plt.subplot(grid[2, 4])\n",
"plt.plot(x_sim[2, :])\n",
"plt.ylabel(\"theta(t) [rad]\")\n",
"\n",
"plt.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [],
"metadata": {
"collapsed": false
}
}
],
"metadata": {
"kernelspec": {
"name": "python3",
"language": "python",
"display_name": "Python 3 (ipykernel)"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.5"
}
},
"nbformat": 4,
"nbformat_minor": 4
}