# Quintic Polynomial Planner We can describe one-dimensional robot motion as a quintic polynomial based on distance as $x(s) = a_0 + a_1s + a_2s^2 + a_3s^3 + a_4s^4 + a_5s^5$ The boundary conditions are $x_i, v_i, a_i, x_f, v_f, a_f$ where $x_i=\text{initial position}$ $v_i=\text{initial velocity}$ $a_i=\text{initial acceleration}$ $x_f=\text{final position}$ $v_f=\text{final velocity}$ $a_f=\text{final acceleration}$ When $s=0$, the polynomial can be represented as $x(0)=a_0$ $\therefore x_i=a_0$ To solve for velocity, we can differentiate the polynomial and represent it as $\frac{dx}{dt}=a_1+2a_2s+3a_3s^2+4a_4s^3+5a_5s^4$ We can also solve the equation for initial velocity similarly, where $s=0$, represented by the following as $\frac{dx}{dt}=a_1$ $\therefore v_i=a_1$ It is now obvious that the initial acceleration can be represented as $\frac{d^2x}{dt^2}=2a_2$ $\therefore a_i=2a_2$ To solve for the remaining three unknowns, we can assume that the final distance for a manoeuvre is $S$ and thus, represented as $x(S)=a_0+a_1S+a_2S^2+a_3S^3+a_4S^4+a_5S^5$ $\frac{dx}{dS}=a_1+2a_2S+3a_3S^2+4a_4S^3+5a_5S^4$ $\frac{d^2x}{dS^2}=2a_2+6a_3S+12a_4S^2+20a_5S^3$ We can represent these three equations in the form of a linear equation to solve for $a_3, a_4, a_5$ $\begin{bmatrix} S^3 & S^4 & S^5 \\ 3S^2 & 4S^3 & 5S^4 \\ 6S & 12S^2 & 20S^3 \end{bmatrix} \begin{bmatrix} a_3 \\ a_4 \\ a_5 \end{bmatrix} = \begin{bmatrix} x_f-x_i-v_iS-0.5a_iS^2 & v_f-v_i-a_iS & a_f-a_i \end{bmatrix}$ However, for two-dimensional robot motion, there are now two more terminal states, $\theta_i$ and $\theta_f$ where $\theta_i=\text{initial orientation}$ $\theta_f=\text{final orientation}$ Each velocity and acceleration boundary condition can be calculated with each orientation, represented as $v_{xi}=v_i\cos(\theta_i), v_{xf}=v_f\cos(\theta_f)$ $v_{yi}=v_i\sin(\theta_i), v_{yf}=v_f\sin(\theta_f)$ $a_{xi}=a_i\cos(\theta_i), a_{xf}=a_f\cos(\theta_f)$ $a_{yi}=a_i\sin(\theta_i), a_{yf}=a_f\sin(\theta_f)$ My python implementation for generating a quintic polynomial trajectory: ```python=1 import numpy as np class QuinticPolynomial: def __init__(self, init_pos, init_vel, init_accel, final_pos, final_vel, final_accel, dist): # Derived coefficients self.a_0 = init_pos self.a_1 = init_vel self.a_2 = init_accel / 2.0 # Solve the linear equation (Ax = B) A = np.array([[dist ** 3, dist ** 4, dist ** 5], [3 * dist ** 2, 4 * dist ** 3, 5 * dist ** 4], [6 * dist, 12 * dist ** 2, 20 * dist ** 3]]) B = np.array([final_pos - self.a_0 - self.a_1 * dist - self.a_2 * dist ** 2, final_vel - self.a_1 - init_accel * dist, final_accel - init_accel]) x = np.linalg.solve(A, B) self.a_3 = x[0] self.a_4 = x[1] self.a_5 = x[2] def calc_point(self, s): xs = self.a_0 + self.a_1 * s + self.a_2 * s ** 2 + self.a_3 * s ** 3 + self.a_4 * s ** 4 + self.a_5 * s ** 5 return xs def calc_first_derivative(self, s): xs = self.a_1 + 2 * self.a_2 * s + 3 * self.a_3 * s ** 2 + 4 * self.a_4 * s ** 3 + 5 * self.a_5 * s ** 4 return xs def calc_second_derivative(self, s): xs = 2 * self.a_2 + 6 * self.a_3 * s + 12 * self.a_4 * s ** 2 + 20 * self.a_5 * s ** 3 return xs def calc_third_derivative(self, s): xs = 6 * self.a_3 + 24 * self.a_4 * s + 60 * self.a_5 * s ** 2 return xs def quintic_polynomial_planner(x_i, y_i, yaw_i, v_i, a_i, x_f, y_f, yaw_f, v_f, a_f, max_accel, max_jerk, ds): # Calculate the velocity boundary conditions based on vehicle's orientation v_xi = v_i * np.cos(yaw_i) v_xf = v_f * np.cos(yaw_f) v_yi = v_i * np.sin(yaw_i) v_yf = v_f * np.sin(yaw_f) # Calculate the acceleration boundary conditions based on vehicle's orientation a_xi = a_i * np.cos(yaw_i) a_xf = a_f * np.cos(yaw_f) a_yi = a_i * np.sin(yaw_i) a_yf = a_f * np.sin(yaw_f) for S in np.arange(1.0, 1000.0, 1.0): # Initialise the class xqp = QuinticPolynomial(x_i, v_xi, a_xi, x_f, v_xf, a_xf, S) yqp = QuinticPolynomial(y_i, v_yi, a_yi, y_f, v_yf, a_yf, S) # Instantiate/clear the arrays x = [] y = [] v = [] a = [] j = [] yaw = [] for s in np.arange(0.0, S + ds, ds): # Solve for position (m) x.append(xqp.calc_point(s)) y.append(yqp.calc_point(s)) # Solve for velocity (m/s) v_x = xqp.calc_first_derivative(s) v_y = yqp.calc_first_derivative(s) v.append(np.hypot(v_x, v_y)) # Solve for orientation (rad) yaw.append(np.arctan2(v_y, v_x)) # Solve for acceleration (m/s^2) a_x = xqp.calc_second_derivative(s) a_y = yqp.calc_second_derivative(s) if len(v) >= 2 and v[-1] - v[-2] < 0.0: a.append(-1.0 * np.hypot(a_x, a_y)) else: a.append(np.hypot(a_x, a_y)) # Solve for jerk (m/s^3) j_x = xqp.calc_third_derivative(s) j_y = yqp.calc_third_derivative(s) if len(a) >= 2 and a[-1] - a[-2] < 0.0: j.append(-1 * np.hypot(j_x, j_y)) else: j.append(np.hypot(j_x, j_y)) if max([abs(i) for i in a]) <= max_accel and max([abs(i) for i in j]) <= max_jerk: break return x, y, v, a, j, yaw ```