2021-04-13 18:30:08 +08:00
|
|
|
{
|
|
|
|
"cells": [
|
|
|
|
{
|
|
|
|
"cell_type": "code",
|
|
|
|
"execution_count": 1,
|
|
|
|
"metadata": {},
|
|
|
|
"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",
|
2022-08-02 16:33:49 +08:00
|
|
|
"\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"plt.style.use(\"ggplot\")\n",
|
|
|
|
"\n",
|
|
|
|
"import time"
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"cell_type": "markdown",
|
|
|
|
"metadata": {},
|
|
|
|
"source": [
|
|
|
|
"## V2 Use Dynamics w.r.t Robot Frame\n",
|
|
|
|
"\n",
|
|
|
|
"explanation here...\n",
|
|
|
|
"\n",
|
|
|
|
"benefits:\n",
|
|
|
|
"* slightly faster mpc convergence time -> more variables are 0, this helps the computation?\n",
|
|
|
|
"* no issues when vehicle heading ~PI in world"
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"cell_type": "code",
|
|
|
|
"execution_count": 2,
|
|
|
|
"metadata": {},
|
|
|
|
"outputs": [],
|
|
|
|
"source": [
|
|
|
|
"\"\"\"\n",
|
|
|
|
"Control problem statement.\n",
|
|
|
|
"\"\"\"\n",
|
|
|
|
"\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"N = 4 # number of state variables\n",
|
|
|
|
"M = 2 # number of control variables\n",
|
|
|
|
"T = 20 # Prediction Horizon\n",
|
|
|
|
"DT = 0.2 # discretization step\n",
|
|
|
|
"\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"def get_linear_model(x_bar, u_bar):\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" \"\"\"\n",
|
|
|
|
" Computes the LTI approximated state space model x' = Ax + Bu + C\n",
|
|
|
|
" \"\"\"\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"\n",
|
|
|
|
" L = 0.3 # vehicle wheelbase\n",
|
|
|
|
"\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" x = x_bar[0]\n",
|
|
|
|
" y = x_bar[1]\n",
|
|
|
|
" v = x_bar[2]\n",
|
|
|
|
" theta = x_bar[3]\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" a = u_bar[0]\n",
|
|
|
|
" delta = u_bar[1]\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"\n",
|
|
|
|
" A = np.zeros((N, N))\n",
|
|
|
|
" A[0, 2] = np.cos(theta)\n",
|
|
|
|
" A[0, 3] = -v * np.sin(theta)\n",
|
|
|
|
" A[1, 2] = np.sin(theta)\n",
|
|
|
|
" A[1, 3] = v * np.cos(theta)\n",
|
|
|
|
" A[3, 2] = v * np.tan(delta) / L\n",
|
|
|
|
" A_lin = np.eye(N) + DT * A\n",
|
|
|
|
"\n",
|
|
|
|
" B = np.zeros((N, M))\n",
|
|
|
|
" B[2, 0] = 1\n",
|
|
|
|
" B[3, 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), a, 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)\n",
|
|
|
|
"\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
|
|
|
"\"\"\"\n",
|
|
|
|
"the ODE is used to update the simulation given the mpc results\n",
|
|
|
|
"I use this insted of using the LTI twice\n",
|
|
|
|
"\"\"\"\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"\n",
|
|
|
|
"\n",
|
|
|
|
"def kinematics_model(x, t, u):\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" \"\"\"\n",
|
|
|
|
" Returns the set of ODE of the vehicle model.\n",
|
|
|
|
" \"\"\"\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"\n",
|
|
|
|
" L = 0.3 # vehicle wheelbase\n",
|
|
|
|
" dxdt = x[2] * np.cos(x[3])\n",
|
|
|
|
" dydt = x[2] * np.sin(x[3])\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" dvdt = u[0]\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" dthetadt = x[2] * np.tan(u[1]) / L\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" dqdt = [dxdt, dydt, dvdt, dthetadt]\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
|
|
|
" return dqdt\n",
|
|
|
|
"\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"\n",
|
|
|
|
"def predict(x0, u):\n",
|
|
|
|
" \"\"\" \"\"\"\n",
|
|
|
|
"\n",
|
|
|
|
" x_ = np.zeros((N, T + 1))\n",
|
|
|
|
"\n",
|
|
|
|
" x_[:, 0] = x0\n",
|
|
|
|
"\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" # solve ODE\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" for t in range(1, T + 1):\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" tspan = [0, DT]\n",
|
|
|
|
" x_next = odeint(kinematics_model, x0, tspan, args=(u[:, t - 1],))\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
|
|
|
" x0 = x_next[1]\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" x_[:, t] = x_next[1]\n",
|
|
|
|
"\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" return x_\n",
|
|
|
|
"\n",
|
|
|
|
"\n",
|
|
|
|
"\"\"\"\n",
|
|
|
|
"MODIFIED TO INCLUDE FRAME TRANSFORMATION\n",
|
|
|
|
"\"\"\"\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"\n",
|
|
|
|
"\n",
|
|
|
|
"def compute_path_from_wp(start_xp, start_yp, step=0.1):\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" \"\"\"\n",
|
|
|
|
" Computes a reference path given a set of waypoints\n",
|
|
|
|
" \"\"\"\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"\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",
|
2021-04-13 18:30:08 +08:00
|
|
|
" # watch out to duplicate points!\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" final_xp = np.append(final_xp, fx(interp_range)[1:])\n",
|
|
|
|
" final_yp = np.append(final_yp, fy(interp_range)[1:])\n",
|
|
|
|
"\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" 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",
|
2022-08-02 16:33:49 +08:00
|
|
|
" return np.vstack((final_xp, final_yp, theta))\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
|
|
|
"\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"def get_nn_idx(state, path):\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" \"\"\"\n",
|
|
|
|
" Computes the index of the waypoint closest to vehicle\n",
|
|
|
|
" \"\"\"\n",
|
|
|
|
"\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" dx = state[0] - path[0, :]\n",
|
|
|
|
" dy = state[1] - path[1, :]\n",
|
|
|
|
" dist = np.hypot(dx, dy)\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" nn_idx = np.argmin(dist)\n",
|
|
|
|
"\n",
|
|
|
|
" try:\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" v = [\n",
|
|
|
|
" path[0, nn_idx + 1] - path[0, nn_idx],\n",
|
|
|
|
" path[1, nn_idx + 1] - path[1, nn_idx],\n",
|
|
|
|
" ]\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" v /= np.linalg.norm(v)\n",
|
|
|
|
"\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" d = [path[0, nn_idx] - state[0], path[1, nn_idx] - state[1]]\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" if np.dot(d, v) > 0:\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" target_idx = nn_idx\n",
|
|
|
|
" else:\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" target_idx = nn_idx + 1\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
|
|
|
" except IndexError as e:\n",
|
|
|
|
" target_idx = nn_idx\n",
|
|
|
|
"\n",
|
|
|
|
" return target_idx\n",
|
|
|
|
"\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"def normalize_angle(angle):\n",
|
|
|
|
" \"\"\"\n",
|
|
|
|
" Normalize an angle to [-pi, pi]\n",
|
|
|
|
" \"\"\"\n",
|
|
|
|
" while angle > np.pi:\n",
|
|
|
|
" angle -= 2.0 * np.pi\n",
|
|
|
|
"\n",
|
|
|
|
" while angle < -np.pi:\n",
|
|
|
|
" angle += 2.0 * np.pi\n",
|
|
|
|
"\n",
|
|
|
|
" return angle\n",
|
|
|
|
"\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"def get_ref_trajectory(state, path, target_v):\n",
|
|
|
|
" \"\"\"\n",
|
|
|
|
" modified reference in robot frame\n",
|
|
|
|
" \"\"\"\n",
|
|
|
|
" xref = np.zeros((N, T + 1))\n",
|
|
|
|
" dref = np.zeros((1, T + 1))\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"\n",
|
|
|
|
" # sp = np.ones((1,T +1))*target_v #speed profile\n",
|
|
|
|
"\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" ncourse = path.shape[1]\n",
|
|
|
|
"\n",
|
|
|
|
" ind = get_nn_idx(state, path)\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" dx = path[0, ind] - state[0]\n",
|
|
|
|
" dy = path[1, ind] - state[1]\n",
|
|
|
|
"\n",
|
|
|
|
" xref[0, 0] = dx * np.cos(-state[3]) - dy * np.sin(-state[3]) # X\n",
|
|
|
|
" xref[1, 0] = dy * np.cos(-state[3]) + dx * np.sin(-state[3]) # Y\n",
|
|
|
|
" xref[2, 0] = target_v # V\n",
|
|
|
|
" xref[3, 0] = normalize_angle(path[2, ind] - state[3]) # Theta\n",
|
|
|
|
" dref[0, 0] = 0.0 # steer operational point should be 0\n",
|
|
|
|
"\n",
|
|
|
|
" dl = 0.05 # Waypoints spacing [m]\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" travel = 0.0\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" for i in range(T + 1):\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" travel += abs(target_v) * DT # current V or target V?\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" dind = int(round(travel / dl))\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" if (ind + dind) < ncourse:\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" dx = path[0, ind + dind] - state[0]\n",
|
|
|
|
" dy = path[1, ind + dind] - state[1]\n",
|
|
|
|
"\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" xref[0, i] = dx * np.cos(-state[3]) - dy * np.sin(-state[3])\n",
|
|
|
|
" xref[1, i] = dy * np.cos(-state[3]) + dx * np.sin(-state[3])\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" xref[2, i] = target_v # sp[ind + dind]\n",
|
|
|
|
" xref[3, i] = normalize_angle(path[2, ind + dind] - state[3])\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" dref[0, i] = 0.0\n",
|
|
|
|
" else:\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" dx = path[0, ncourse - 1] - state[0]\n",
|
|
|
|
" dy = path[1, ncourse - 1] - state[1]\n",
|
|
|
|
"\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" xref[0, i] = dx * np.cos(-state[3]) - dy * np.sin(-state[3])\n",
|
|
|
|
" xref[1, i] = dy * np.cos(-state[3]) + dx * np.sin(-state[3])\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" xref[2, i] = 0.0 # stop? #sp[ncourse - 1]\n",
|
|
|
|
" xref[3, i] = normalize_angle(path[2, ncourse - 1] - state[3])\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" dref[0, i] = 0.0\n",
|
|
|
|
"\n",
|
|
|
|
" return xref, dref"
|
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"cell_type": "code",
|
|
|
|
"execution_count": 3,
|
|
|
|
"metadata": {},
|
|
|
|
"outputs": [
|
|
|
|
{
|
|
|
|
"name": "stdout",
|
|
|
|
"output_type": "stream",
|
|
|
|
"text": [
|
|
|
|
"CVXPY Optimization Time: Avrg: 0.1655s Max: 0.1952s Min: 0.1495s\n"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
],
|
|
|
|
"source": [
|
2022-08-02 16:33:49 +08:00
|
|
|
"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",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
|
|
|
"# track = compute_path_from_wp([0,10,10,0],\n",
|
|
|
|
"# [0,0,1,1],0.05)\n",
|
|
|
|
"\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"sim_duration = 200 # time steps\n",
|
|
|
|
"opt_time = []\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"x_sim = np.zeros((N, sim_duration))\n",
|
|
|
|
"u_sim = np.zeros((M, sim_duration - 1))\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"MAX_SPEED = 1.5 # m/s\n",
|
|
|
|
"MAX_ACC = 1.0 # m/ss\n",
|
|
|
|
"MAX_D_ACC = 1.0 # m/sss\n",
|
|
|
|
"MAX_STEER = np.radians(30) # rad\n",
|
|
|
|
"MAX_D_STEER = np.radians(30) # rad/s\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"REF_VEL = 1.0 # m/s\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
|
|
|
"# Starting Condition\n",
|
|
|
|
"x0 = np.zeros(N)\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"x0[0] = 0 # x\n",
|
|
|
|
"x0[1] = -0.25 # y\n",
|
|
|
|
"x0[2] = 0.0 # v\n",
|
|
|
|
"x0[3] = np.radians(-0) # yaw\n",
|
|
|
|
"\n",
|
|
|
|
"# starting guess\n",
|
|
|
|
"u_bar = np.zeros((M, T))\n",
|
|
|
|
"u_bar[0, :] = MAX_ACC / 2 # a\n",
|
|
|
|
"u_bar[1, :] = 0.0 # delta\n",
|
|
|
|
"\n",
|
|
|
|
"for sim_time in range(sim_duration - 1):\n",
|
|
|
|
"\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" iter_start = time.time()\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"\n",
|
|
|
|
" # dynamics starting state w.r.t. robot are always null except vel\n",
|
|
|
|
" x_bar = np.zeros((N, T + 1))\n",
|
|
|
|
" x_bar[2, 0] = x_sim[2, sim_time]\n",
|
|
|
|
"\n",
|
|
|
|
" # prediction for linearization of costrains\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",
|
|
|
|
" x = cp.Variable((N, T + 1))\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" u = cp.Variable((M, T))\n",
|
|
|
|
" cost = 0\n",
|
|
|
|
" constr = []\n",
|
|
|
|
"\n",
|
|
|
|
" # Cost Matrices\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" Q = np.diag([20, 20, 10, 20]) # state error cost\n",
|
|
|
|
" Qf = np.diag([30, 30, 30, 30]) # 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",
|
|
|
|
" # dont use x0 in this case\n",
|
|
|
|
" x_ref, d_ref = get_ref_trajectory(x_sim[:, sim_time], track, REF_VEL)\n",
|
|
|
|
"\n",
|
|
|
|
" # Prediction Horizon\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" for t in range(T):\n",
|
|
|
|
"\n",
|
|
|
|
" # Tracking Error\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" cost += cp.quad_form(x[:, t] - x_ref[:, t], Q)\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
|
|
|
" # Actuation effort\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" cost += cp.quad_form(u[:, t], R)\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
|
|
|
" # Actuation rate of change\n",
|
|
|
|
" if t < (T - 1):\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" 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",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
|
|
|
" # Kinrmatics Constrains (Linearized model)\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" 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",
|
2021-04-13 18:30:08 +08:00
|
|
|
" cost += cp.quad_form(x[:, T] - x_ref[:, T], Qf)\n",
|
|
|
|
"\n",
|
|
|
|
" # sums problem objectives and concatenates constraints.\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" constr += [x[:, 0] == x_bar[:, 0]] # starting condition\n",
|
|
|
|
" constr += [x[2, :] <= MAX_SPEED] # max speed\n",
|
|
|
|
" constr += [x[2, :] >= 0.0] # min_speed (not really needed)\n",
|
|
|
|
" constr += [cp.abs(u[0, :]) <= MAX_ACC] # max acc\n",
|
|
|
|
" constr += [cp.abs(u[1, :]) <= MAX_STEER] # max steer\n",
|
|
|
|
"\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" # Solve\n",
|
|
|
|
" prob = cp.Problem(cp.Minimize(cost), constr)\n",
|
|
|
|
" solution = prob.solve(solver=cp.OSQP, verbose=False)\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"\n",
|
|
|
|
" # retrieved optimized U and assign to u_bar to linearize in next step\n",
|
|
|
|
" u_bar = np.vstack(\n",
|
|
|
|
" (np.array(u.value[0, :]).flatten(), (np.array(u.value[1, :]).flatten()))\n",
|
|
|
|
" )\n",
|
|
|
|
"\n",
|
|
|
|
" u_sim[:, sim_time] = u_bar[:, 0]\n",
|
|
|
|
"\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" # Measure elpased time to get results from cvxpy\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" opt_time.append(time.time() - iter_start)\n",
|
|
|
|
"\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
" # move simulation to t+1\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
" tspan = [0, DT]\n",
|
|
|
|
" x_sim[:, sim_time + 1] = odeint(\n",
|
|
|
|
" kinematics_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",
|
|
|
|
")"
|
2021-04-13 18:30:08 +08:00
|
|
|
]
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"cell_type": "code",
|
|
|
|
"execution_count": 4,
|
|
|
|
"metadata": {},
|
|
|
|
"outputs": [
|
|
|
|
{
|
|
|
|
"data": {
|
|
|
|
"image/png": "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
|
|
|
|
"text/plain": [
|
|
|
|
"<Figure size 1080x720 with 5 Axes>"
|
|
|
|
]
|
|
|
|
},
|
|
|
|
"metadata": {},
|
|
|
|
"output_type": "display_data"
|
|
|
|
}
|
|
|
|
],
|
|
|
|
"source": [
|
2022-08-02 16:33:49 +08:00
|
|
|
"# plot trajectory\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"grid = plt.GridSpec(4, 5)\n",
|
|
|
|
"\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"plt.figure(figsize=(15, 10))\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
|
|
|
"plt.subplot(grid[0:4, 0:4])\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"plt.plot(track[0, :], track[1, :], \"b+\")\n",
|
|
|
|
"plt.plot(x_sim[0, :], x_sim[1, :])\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"plt.axis(\"equal\")\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"plt.ylabel(\"y\")\n",
|
|
|
|
"plt.xlabel(\"x\")\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
|
|
|
"plt.subplot(grid[0, 4])\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"plt.plot(u_sim[0, :])\n",
|
|
|
|
"plt.ylabel(\"a(t) [m/ss]\")\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
|
|
|
"plt.subplot(grid[1, 4])\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"plt.plot(x_sim[2, :])\n",
|
|
|
|
"plt.ylabel(\"v(t) [m/s]\")\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
|
|
|
"plt.subplot(grid[2, 4])\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"plt.plot(np.degrees(u_sim[1, :]))\n",
|
|
|
|
"plt.ylabel(\"delta(t) [rad]\")\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
|
|
|
"plt.subplot(grid[3, 4])\n",
|
2022-08-02 16:33:49 +08:00
|
|
|
"plt.plot(x_sim[3, :])\n",
|
|
|
|
"plt.ylabel(\"theta(t) [rad]\")\n",
|
2021-04-13 18:30:08 +08:00
|
|
|
"\n",
|
|
|
|
"plt.tight_layout()\n",
|
|
|
|
"plt.show()"
|
|
|
|
]
|
|
|
|
}
|
|
|
|
],
|
|
|
|
"metadata": {
|
|
|
|
"kernelspec": {
|
|
|
|
"display_name": "Python [conda env:.conda-jupyter] *",
|
|
|
|
"language": "python",
|
|
|
|
"name": "conda-env-.conda-jupyter-py"
|
|
|
|
},
|
|
|
|
"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
|
|
|
|
}
|