2020-01-13 20:25:26 +08:00
|
|
|
#! /usr/bin/env python
|
|
|
|
|
|
|
|
import numpy as np
|
|
|
|
import matplotlib.pyplot as plt
|
|
|
|
from matplotlib import animation
|
2022-02-05 18:16:38 +08:00
|
|
|
from scipy.integrate import odeint
|
2020-01-13 20:25:26 +08:00
|
|
|
|
2021-07-08 19:54:48 +08:00
|
|
|
from mpcpy.utils import compute_path_from_wp
|
|
|
|
import mpcpy
|
2020-01-13 20:25:26 +08:00
|
|
|
|
|
|
|
import sys
|
|
|
|
import time
|
|
|
|
|
2020-01-14 21:37:28 +08:00
|
|
|
# Robot Starting position
|
2022-02-05 18:16:38 +08:00
|
|
|
SIM_START_X = 0.0
|
|
|
|
SIM_START_Y = 0.5
|
|
|
|
SIM_START_V = 0.0
|
|
|
|
SIM_START_H = 0.0
|
|
|
|
L = 0.3
|
2020-01-13 22:33:29 +08:00
|
|
|
|
2023-10-13 03:17:55 +08:00
|
|
|
params = mpcpy.Params()
|
2020-07-01 00:21:27 +08:00
|
|
|
|
2022-02-05 18:16:38 +08:00
|
|
|
# Params
|
|
|
|
VEL = 1.0 # m/s
|
2020-01-13 20:25:26 +08:00
|
|
|
|
2022-02-05 18:16:38 +08:00
|
|
|
# Classes
|
|
|
|
class MPCSim:
|
2020-01-13 20:25:26 +08:00
|
|
|
def __init__(self):
|
|
|
|
|
|
|
|
# State for the robot mathematical model [x,y,heading]
|
2022-02-05 18:16:38 +08:00
|
|
|
self.state = np.array([SIM_START_X, SIM_START_Y, SIM_START_V, SIM_START_H])
|
2021-07-08 19:54:48 +08:00
|
|
|
|
2022-02-05 18:16:38 +08:00
|
|
|
# starting guess
|
2023-10-13 03:17:55 +08:00
|
|
|
self.action = np.zeros(params.M)
|
|
|
|
self.action[0] = params.MAX_ACC / 2 # a
|
2022-02-05 18:16:38 +08:00
|
|
|
self.action[1] = 0.0 # delta
|
|
|
|
|
2023-10-13 03:17:55 +08:00
|
|
|
self.K = int(params.T / params.DT)
|
|
|
|
self.opt_u = np.zeros((params.M, self.K))
|
2021-07-08 19:54:48 +08:00
|
|
|
|
|
|
|
# Cost Matrices
|
2023-10-13 03:17:55 +08:00
|
|
|
Q = [20, 20, 10, 20] # state error cost
|
|
|
|
Qf = [30, 30, 30, 30] # state final error cost
|
|
|
|
R = [10, 10] # input cost
|
|
|
|
P = [10, 10] # input rate of change cost
|
2022-02-05 18:16:38 +08:00
|
|
|
|
2023-10-13 03:17:55 +08:00
|
|
|
self.mpc = mpcpy.MPC(Q, Qf, R, P)
|
2020-01-13 20:25:26 +08:00
|
|
|
|
|
|
|
# Interpolated Path to follow given waypoints
|
2022-02-05 18:16:38 +08:00
|
|
|
self.path = compute_path_from_wp(
|
|
|
|
[0, 3, 4, 6, 10, 12, 13, 13, 6, 1, 0],
|
|
|
|
[0, 0, 2, 4, 3, 3, -1, -2, -6, -2, -2],
|
2023-10-13 03:17:55 +08:00
|
|
|
0.05,
|
2022-02-05 18:16:38 +08:00
|
|
|
)
|
2020-01-13 20:25:26 +08:00
|
|
|
|
2020-01-13 22:33:29 +08:00
|
|
|
# Sim help vars
|
2022-02-05 18:16:38 +08:00
|
|
|
self.sim_time = 0
|
|
|
|
self.x_history = []
|
|
|
|
self.y_history = []
|
|
|
|
self.v_history = []
|
|
|
|
self.h_history = []
|
|
|
|
self.a_history = []
|
|
|
|
self.d_history = []
|
|
|
|
self.predicted = None
|
|
|
|
|
|
|
|
# Initialise plot
|
2020-01-13 20:25:26 +08:00
|
|
|
plt.style.use("ggplot")
|
|
|
|
self.fig = plt.figure()
|
|
|
|
plt.ion()
|
|
|
|
plt.show()
|
|
|
|
|
2022-02-05 18:16:38 +08:00
|
|
|
def preview(self, mpc_out):
|
|
|
|
"""
|
|
|
|
[TODO:summary]
|
|
|
|
|
|
|
|
[TODO:description]
|
|
|
|
"""
|
|
|
|
predicted = np.zeros(self.opt_u.shape)
|
|
|
|
predicted[:, :] = mpc_out[0:2, 1:]
|
|
|
|
Rotm = np.array(
|
|
|
|
[
|
|
|
|
[np.cos(self.state[3]), np.sin(self.state[3])],
|
|
|
|
[-np.sin(self.state[3]), np.cos(self.state[3])],
|
|
|
|
]
|
|
|
|
)
|
|
|
|
predicted = (predicted.T.dot(Rotm)).T
|
|
|
|
predicted[0, :] += self.state[0]
|
|
|
|
predicted[1, :] += self.state[1]
|
2020-05-05 00:36:23 +08:00
|
|
|
self.predicted = predicted
|
|
|
|
|
2020-01-13 20:25:26 +08:00
|
|
|
def run(self):
|
2022-02-05 18:16:38 +08:00
|
|
|
"""
|
|
|
|
[TODO:summary]
|
|
|
|
|
|
|
|
[TODO:description]
|
|
|
|
"""
|
2020-11-02 22:44:31 +08:00
|
|
|
self.plot_sim()
|
|
|
|
input("Press Enter to continue...")
|
2020-01-13 20:25:26 +08:00
|
|
|
while 1:
|
2022-02-05 18:16:38 +08:00
|
|
|
if (
|
|
|
|
np.sqrt(
|
|
|
|
(self.state[0] - self.path[0, -1]) ** 2
|
|
|
|
+ (self.state[1] - self.path[1, -1]) ** 2
|
|
|
|
)
|
|
|
|
< 0.5
|
|
|
|
):
|
2021-07-08 19:54:48 +08:00
|
|
|
print("Success! Goal Reached")
|
|
|
|
input("Press Enter to continue...")
|
|
|
|
return
|
2022-02-05 18:16:38 +08:00
|
|
|
# optimization loop
|
|
|
|
# start=time.time()
|
|
|
|
# dynamycs w.r.t robot frame
|
2022-07-21 19:23:08 +08:00
|
|
|
curr_state = np.array([0, 0, self.state[2], 0])
|
2021-07-08 19:54:48 +08:00
|
|
|
# State Matrices
|
2022-02-05 18:16:38 +08:00
|
|
|
A, B, C = mpcpy.get_linear_model_matrices(curr_state, self.action)
|
|
|
|
# Get Reference_traj -> inputs are in worldframe
|
2023-10-13 03:17:55 +08:00
|
|
|
target, _ = mpcpy.get_ref_trajectory(self.state, self.path, VEL)
|
2022-02-05 18:16:38 +08:00
|
|
|
|
|
|
|
x_mpc, u_mpc = self.mpc.optimize_linearized_model(
|
|
|
|
A,
|
|
|
|
B,
|
|
|
|
C,
|
|
|
|
curr_state,
|
|
|
|
target,
|
|
|
|
verbose=False,
|
|
|
|
)
|
2023-10-13 03:17:55 +08:00
|
|
|
# NOTE: used only for preview purposes
|
2022-02-05 18:16:38 +08:00
|
|
|
self.opt_u = np.vstack(
|
|
|
|
(
|
|
|
|
np.array(u_mpc.value[0, :]).flatten(),
|
2023-10-13 03:17:55 +08:00
|
|
|
np.array(u_mpc.value[1, :]).flatten(),
|
2022-02-05 18:16:38 +08:00
|
|
|
)
|
|
|
|
)
|
|
|
|
self.action[:] = [u_mpc.value[0, 0], u_mpc.value[1, 0]]
|
2021-07-08 19:54:48 +08:00
|
|
|
# print("CVXPY Optimization Time: {:.4f}s".format(time.time()-start))
|
2022-02-05 18:16:38 +08:00
|
|
|
self.predict([self.action[0], self.action[1]])
|
|
|
|
self.preview(x_mpc.value)
|
2021-07-08 19:54:48 +08:00
|
|
|
self.plot_sim()
|
2022-07-21 19:23:08 +08:00
|
|
|
|
2022-02-05 18:16:38 +08:00
|
|
|
def predict(self, u):
|
|
|
|
def kinematics_model(x, t, u):
|
|
|
|
dxdt = x[2] * np.cos(x[3])
|
|
|
|
dydt = x[2] * np.sin(x[3])
|
|
|
|
dvdt = u[0]
|
2023-10-13 03:17:55 +08:00
|
|
|
dtheta0dt = x[2] * np.tan(u[1]) / params.L
|
2022-02-05 18:16:38 +08:00
|
|
|
dqdt = [dxdt, dydt, dvdt, dtheta0dt]
|
|
|
|
return dqdt
|
|
|
|
|
|
|
|
# solve ODE
|
2023-10-13 03:17:55 +08:00
|
|
|
tspan = [0, params.DT]
|
2022-02-05 18:16:38 +08:00
|
|
|
self.state = odeint(kinematics_model, self.state, tspan, args=(u[:],))[1]
|
2020-01-13 20:25:26 +08:00
|
|
|
|
|
|
|
def plot_sim(self):
|
2022-02-05 18:16:38 +08:00
|
|
|
"""
|
|
|
|
[TODO:summary]
|
|
|
|
|
|
|
|
[TODO:description]
|
|
|
|
"""
|
2023-10-13 03:17:55 +08:00
|
|
|
self.sim_time = self.sim_time + params.DT
|
2020-01-13 22:33:29 +08:00
|
|
|
self.x_history.append(self.state[0])
|
|
|
|
self.y_history.append(self.state[1])
|
2020-07-01 22:59:13 +08:00
|
|
|
self.v_history.append(self.state[2])
|
|
|
|
self.h_history.append(self.state[3])
|
2022-02-05 18:16:38 +08:00
|
|
|
self.a_history.append(self.opt_u[0, 1])
|
|
|
|
self.d_history.append(self.opt_u[1, 1])
|
2020-01-13 22:33:29 +08:00
|
|
|
|
2020-01-13 20:25:26 +08:00
|
|
|
plt.clf()
|
2020-01-13 22:33:29 +08:00
|
|
|
|
|
|
|
grid = plt.GridSpec(2, 3)
|
2020-01-13 20:25:26 +08:00
|
|
|
|
2020-01-14 21:37:28 +08:00
|
|
|
plt.subplot(grid[0:2, 0:2])
|
2022-02-05 18:16:38 +08:00
|
|
|
plt.title(
|
|
|
|
"MPC Simulation \n" + "Simulation elapsed time {}s".format(self.sim_time)
|
|
|
|
)
|
|
|
|
|
|
|
|
plt.plot(
|
|
|
|
self.path[0, :],
|
|
|
|
self.path[1, :],
|
|
|
|
c="tab:orange",
|
|
|
|
marker=".",
|
|
|
|
label="reference track",
|
|
|
|
)
|
|
|
|
|
|
|
|
plt.plot(
|
|
|
|
self.x_history,
|
|
|
|
self.y_history,
|
|
|
|
c="tab:blue",
|
|
|
|
marker=".",
|
|
|
|
alpha=0.5,
|
|
|
|
label="vehicle trajectory",
|
|
|
|
)
|
2020-03-04 20:32:29 +08:00
|
|
|
|
2020-05-05 00:36:23 +08:00
|
|
|
if self.predicted is not None:
|
2022-02-05 18:16:38 +08:00
|
|
|
plt.plot(
|
|
|
|
self.predicted[0, :],
|
|
|
|
self.predicted[1, :],
|
|
|
|
c="tab:green",
|
|
|
|
marker="+",
|
|
|
|
alpha=0.5,
|
|
|
|
label="mpc opt trajectory",
|
|
|
|
)
|
2020-05-05 00:36:23 +08:00
|
|
|
|
2020-03-04 20:32:29 +08:00
|
|
|
# plt.plot(self.x_history[-1], self.y_history[-1], c='tab:blue',
|
|
|
|
# marker=".",
|
|
|
|
# markersize=12,
|
|
|
|
# label="vehicle position")
|
|
|
|
# plt.arrow(self.x_history[-1],
|
|
|
|
# self.y_history[-1],
|
|
|
|
# np.cos(self.h_history[-1]),
|
|
|
|
# np.sin(self.h_history[-1]),
|
|
|
|
# color='tab:blue',
|
|
|
|
# width=0.2,
|
|
|
|
# head_length=0.5,
|
|
|
|
# label="heading")
|
|
|
|
|
|
|
|
plot_car(self.x_history[-1], self.y_history[-1], self.h_history[-1])
|
|
|
|
|
2022-02-05 18:16:38 +08:00
|
|
|
plt.ylabel("map y")
|
|
|
|
plt.yticks(
|
|
|
|
np.arange(min(self.path[1, :]) - 1.0, max(self.path[1, :] + 1.0) + 1, 1.0)
|
|
|
|
)
|
|
|
|
plt.xlabel("map x")
|
|
|
|
plt.xticks(
|
|
|
|
np.arange(min(self.path[0, :]) - 1.0, max(self.path[0, :] + 1.0) + 1, 1.0)
|
|
|
|
)
|
2020-07-01 00:21:27 +08:00
|
|
|
plt.axis("equal")
|
2022-02-05 18:16:38 +08:00
|
|
|
# plt.legend()
|
2020-01-13 22:33:29 +08:00
|
|
|
|
|
|
|
plt.subplot(grid[0, 2])
|
2022-02-05 18:16:38 +08:00
|
|
|
# plt.title("Linear Velocity {} m/s".format(self.v_history[-1]))
|
|
|
|
plt.plot(self.a_history, c="tab:orange")
|
2020-03-04 20:32:29 +08:00
|
|
|
locs, _ = plt.xticks()
|
2023-10-13 03:17:55 +08:00
|
|
|
plt.xticks(locs[1:], locs[1:] * params.DT)
|
2022-02-05 18:16:38 +08:00
|
|
|
plt.ylabel("a(t) [m/ss]")
|
|
|
|
plt.xlabel("t [s]")
|
2020-01-13 22:33:29 +08:00
|
|
|
|
|
|
|
plt.subplot(grid[1, 2])
|
2022-02-05 18:16:38 +08:00
|
|
|
# plt.title("Angular Velocity {} m/s".format(self.w_history[-1]))
|
|
|
|
plt.plot(np.degrees(self.d_history), c="tab:orange")
|
|
|
|
plt.ylabel("gamma(t) [deg]")
|
2020-03-04 20:32:29 +08:00
|
|
|
locs, _ = plt.xticks()
|
2023-10-13 03:17:55 +08:00
|
|
|
plt.xticks(locs[1:], locs[1:] * params.DT)
|
2022-02-05 18:16:38 +08:00
|
|
|
plt.xlabel("t [s]")
|
2020-01-13 22:33:29 +08:00
|
|
|
|
|
|
|
plt.tight_layout()
|
|
|
|
|
2020-01-13 20:25:26 +08:00
|
|
|
plt.draw()
|
|
|
|
plt.pause(0.1)
|
|
|
|
|
2020-03-04 20:32:29 +08:00
|
|
|
|
|
|
|
def plot_car(x, y, yaw):
|
2022-02-05 18:16:38 +08:00
|
|
|
"""
|
|
|
|
[TODO:summary]
|
|
|
|
|
|
|
|
[TODO:description]
|
|
|
|
|
|
|
|
Parameters
|
|
|
|
----------
|
|
|
|
x : [TODO:type]
|
|
|
|
[TODO:description]
|
|
|
|
y : [TODO:type]
|
|
|
|
[TODO:description]
|
|
|
|
yaw : [TODO:type]
|
|
|
|
[TODO:description]
|
|
|
|
"""
|
2020-11-02 22:44:31 +08:00
|
|
|
LENGTH = 0.5 # [m]
|
|
|
|
WIDTH = 0.25 # [m]
|
2020-07-01 22:59:13 +08:00
|
|
|
OFFSET = LENGTH # [m]
|
2020-03-04 20:32:29 +08:00
|
|
|
|
2022-02-05 18:16:38 +08:00
|
|
|
outline = np.array(
|
|
|
|
[
|
|
|
|
[-OFFSET, (LENGTH - OFFSET), (LENGTH - OFFSET), -OFFSET, -OFFSET],
|
|
|
|
[WIDTH / 2, WIDTH / 2, -WIDTH / 2, -WIDTH / 2, WIDTH / 2],
|
|
|
|
]
|
|
|
|
)
|
2020-03-04 20:32:29 +08:00
|
|
|
|
2022-02-05 18:16:38 +08:00
|
|
|
Rotm = np.array([[np.cos(yaw), np.sin(yaw)], [-np.sin(yaw), np.cos(yaw)]])
|
2020-03-04 20:32:29 +08:00
|
|
|
|
|
|
|
outline = (outline.T.dot(Rotm)).T
|
|
|
|
|
|
|
|
outline[0, :] += x
|
|
|
|
outline[1, :] += y
|
|
|
|
|
2022-02-05 18:16:38 +08:00
|
|
|
plt.plot(
|
|
|
|
np.array(outline[0, :]).flatten(), np.array(outline[1, :]).flatten(), "tab:blue"
|
|
|
|
)
|
2020-03-04 20:32:29 +08:00
|
|
|
|
|
|
|
|
2020-01-13 20:25:26 +08:00
|
|
|
def do_sim():
|
2021-07-08 19:54:48 +08:00
|
|
|
sim = MPCSim()
|
2020-01-13 20:25:26 +08:00
|
|
|
try:
|
|
|
|
sim.run()
|
|
|
|
except Exception as e:
|
|
|
|
sys.exit(e)
|
|
|
|
|
2022-02-05 18:16:38 +08:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
2020-01-13 20:25:26 +08:00
|
|
|
do_sim()
|