Skip to content

Polynomial

src.python_motion_planning.curve_generation.polynomial_curve.Polynomial

Bases: Curve

Class for polynomial curve generation(Quintic).

Parameters:

Name Type Description Default
step float

Simulation or interpolation size

required
max_acc float

Maximum acceleration

required
max_jerk float

Maximum jerk

required

Examples:

Python Console Session
>>> from python_motion_planning.curve_generation import Polynomial
>>>     points = [(0, 0, 0), (10, 10, -90), (20, 5, 60)]
>>> generator = Polynomial(step, max_acc, max_jerk)
>>> generator.run(points)
Source code in src\python_motion_planning\curve_generation\polynomial_curve.py
Python
class Polynomial(Curve):
    """
    Class for polynomial curve generation(Quintic).

    Parameters:
        step (float): Simulation or interpolation size
        max_acc (float): Maximum acceleration
        max_jerk (float): Maximum jerk

    Examples:
        >>> from python_motion_planning.curve_generation import Polynomial
        >>>	points = [(0, 0, 0), (10, 10, -90), (20, 5, 60)]
        >>> generator = Polynomial(step, max_acc, max_jerk)
        >>> generator.run(points)
    """
    def __init__(self, step: float, max_acc: float, max_jerk: float) -> None:
        super().__init__(step)
        self.max_acc = max_acc
        self.max_jerk = max_jerk
        self.dt = 0.1
        self.t_min = 1
        self.t_max = 30

    def __str__(self) -> str:
        return "Quintic Polynomial Curve"

    class Poly:
        """
        Polynomial interpolation solver
        """
        def __init__(self, state0: tuple, state1: tuple, t: float) -> None:
            x0, v0, a0 = state0
            xt, vt, at = state1

            A = np.array([[t ** 3, t ** 4, t ** 5],
                          [3 * t ** 2, 4 * t ** 3, 5 * t ** 4],
                          [6 * t, 12 * t ** 2, 20 * t ** 3]])
            b = np.array([xt - x0 - v0 * t - a0 * t ** 2 / 2,
                          vt - v0 - a0 * t,
                          at - a0])
            X = np.linalg.solve(A, b)

            # Quintic polynomial coefficient
            self.p0 = x0
            self.p1 = v0
            self.p2 = a0 / 2.0
            self.p3 = X[0]
            self.p4 = X[1]
            self.p5 = X[2]

        def x(self, t):
            return self.p0 + self.p1 * t + self.p2 * t ** 2 + \
                self.p3 * t ** 3 + self.p4 * t ** 4 + self.p5 * t ** 5

        def dx(self, t):
            return self.p1 + 2 * self.p2 * t + 3 * self.p3 * t ** 2 + \
                4 * self.p4 * t ** 3 + 5 * self.p5 * t ** 4

        def ddx(self, t):
            return 2 * self.p2 + 6 * self.p3 * t + 12 * self.p4 * t ** 2 + 20 * self.p5 * t ** 3

        def dddx(self, t):
            return 6 * self.p3 + 24 * self.p4 * t + 60 * self.p5 * t ** 2

    class Trajectory:
        """
        Polynomial interpolation solver
        """
        def __init__(self):
            self.clear()

        def clear(self):
            self.time = []
            self.x = []
            self.y = []
            self.yaw = []
            self.v = []
            self.a = []
            self.jerk = []

        @property
        def size(self):
            assert len(self.time) == len(self.x) and \
                   len(self.x) == len(self.y) and    \
                   len(self.y) == len(self.yaw) and  \
                   len(self.yaw) == len(self.v) and  \
                   len(self.v) == len(self.a) and    \
                   len(self.a) == len(self.jerk),    \
                   "Unequal dimensions of each attribute, this should not happen."
            return len(self.time)

    def generation(self, start_pose: tuple, goal_pose: tuple):
        """
        Generate the polynomial Curve.

        Parameters:
            start_pose (tuple): Initial pose (x, y, yaw)
            goal_pose (tuple): Target pose (x, y, yaw)

        Returns:
            traj (Traj): The first trajectory that satisfies the acceleration and jerk constraint
        """
        sx, sy, syaw, sv, sa = start_pose
        gx, gy, gyaw, gv, ga = goal_pose

        sv_x = sv * math.cos(syaw)
        sv_y = sv * math.sin(syaw)
        gv_x = gv * math.cos(gyaw)
        gv_y = gv * math.sin(gyaw)

        sa_x = sa * math.cos(syaw)
        sa_y = sa * math.sin(syaw)
        ga_x = ga * math.cos(gyaw)
        ga_y = ga * math.sin(gyaw)

        traj = self.Trajectory()

        for T in np.arange(self.t_min, self.t_max, self.step):
            x_psolver = self.Poly((sx, sv_x, sa_x), (gx, gv_x, ga_x), T)
            y_psolver = self.Poly((sy, sv_y, sa_y), (gy, gv_y, ga_y), T)

            for t in np.arange(0.0, T + self.dt, self.dt):
                traj.time.append(t)
                traj.x.append(x_psolver.x(t))
                traj.y.append(y_psolver.x(t))

                vx = x_psolver.dx(t)
                vy = y_psolver.dx(t)
                traj.v.append(math.hypot(vx, vy))
                traj.yaw.append(math.atan2(vy, vx))

                ax = x_psolver.ddx(t)
                ay = y_psolver.ddx(t)
                a = math.hypot(ax, ay)
                if len(traj.v) >= 2 and traj.v[-1] - traj.v[-2] < 0.0:
                    a *= -1
                traj.a.append(a)

                jx = x_psolver.dddx(t)
                jy = y_psolver.dddx(t)
                j = math.hypot(jx, jy)
                if len(traj.a) >= 2 and traj.a[-1] - traj.a[-2] < 0.0:
                    j *= -1
                traj.jerk.append(j)

            if max(np.abs(traj.a)) <= self.max_acc and \
               max(np.abs(traj.jerk)) <= self.max_jerk:
                return traj
            else:
                traj.clear()

        return traj

    def run(self, points: list):
        """
        Running both generation and animation.

        Parameters:
            points (list[tuple]): path points
        """
        assert len(points) >= 2, "Number of points should be at least 2."
        import matplotlib.pyplot as plt

        # generate velocity and acceleration constraints heuristically
        v = [0]
        for i in range(len(points) - 1):
            v.append(1.0)

        a = [(v[i + 1] - v[i]) / 5 for i in range(len(points) - 1)]
        a.append(0)

        # generate curve
        path_x, path_y, path_yaw = [], [], []
        for i in range(len(points) - 1):
            path = self.generation(
				(points[i][0], points[i][1], np.deg2rad(points[i][2]), v[i], a[i]),
				(points[i + 1][0], points[i + 1][1], np.deg2rad(points[i + 1][2]), v[i + 1], a[i + 1])
            )

            for j in range(path.size):
                path_x.append(path.x[j])
                path_y.append(path.y[j])
                path_yaw.append(path.yaw[j])

        # animation
        plt.figure("curve generation")

        # # static
        # plt.plot(path_x, path_y, linewidth=2, c="#1f77b4")
        # for x, y, theta in points:
        #     Plot.plotArrow(x, y, np.deg2rad(theta), 2, 'blueviolet')

        # plt.axis("equal")
        # plt.title(str(self))

        # dynamic
        plt.ion()
        for i in range(len(path_x)):
            plt.clf()
            plt.gcf().canvas.mpl_connect('key_release_event',
                                         lambda event: [exit(0) if event.key == 'escape' else None])
            plt.plot(path_x, path_y, linewidth=2, c="#1f77b4")
            for x, y, theta in points:
                Plot.plotArrow(x, y, np.deg2rad(theta), 2, 'blueviolet')
            Plot.plotCar(path_x[i], path_y[i], path_yaw[i], 1.5, 3, "black")
            plt.axis("equal")
            plt.title(str(self))
            plt.draw()
            plt.pause(0.001)

        plt.show()

Poly

Polynomial interpolation solver

Source code in src\python_motion_planning\curve_generation\polynomial_curve.py
Python
class Poly:
    """
    Polynomial interpolation solver
    """
    def __init__(self, state0: tuple, state1: tuple, t: float) -> None:
        x0, v0, a0 = state0
        xt, vt, at = state1

        A = np.array([[t ** 3, t ** 4, t ** 5],
                      [3 * t ** 2, 4 * t ** 3, 5 * t ** 4],
                      [6 * t, 12 * t ** 2, 20 * t ** 3]])
        b = np.array([xt - x0 - v0 * t - a0 * t ** 2 / 2,
                      vt - v0 - a0 * t,
                      at - a0])
        X = np.linalg.solve(A, b)

        # Quintic polynomial coefficient
        self.p0 = x0
        self.p1 = v0
        self.p2 = a0 / 2.0
        self.p3 = X[0]
        self.p4 = X[1]
        self.p5 = X[2]

    def x(self, t):
        return self.p0 + self.p1 * t + self.p2 * t ** 2 + \
            self.p3 * t ** 3 + self.p4 * t ** 4 + self.p5 * t ** 5

    def dx(self, t):
        return self.p1 + 2 * self.p2 * t + 3 * self.p3 * t ** 2 + \
            4 * self.p4 * t ** 3 + 5 * self.p5 * t ** 4

    def ddx(self, t):
        return 2 * self.p2 + 6 * self.p3 * t + 12 * self.p4 * t ** 2 + 20 * self.p5 * t ** 3

    def dddx(self, t):
        return 6 * self.p3 + 24 * self.p4 * t + 60 * self.p5 * t ** 2

Trajectory

Polynomial interpolation solver

Source code in src\python_motion_planning\curve_generation\polynomial_curve.py
Python
class Trajectory:
    """
    Polynomial interpolation solver
    """
    def __init__(self):
        self.clear()

    def clear(self):
        self.time = []
        self.x = []
        self.y = []
        self.yaw = []
        self.v = []
        self.a = []
        self.jerk = []

    @property
    def size(self):
        assert len(self.time) == len(self.x) and \
               len(self.x) == len(self.y) and    \
               len(self.y) == len(self.yaw) and  \
               len(self.yaw) == len(self.v) and  \
               len(self.v) == len(self.a) and    \
               len(self.a) == len(self.jerk),    \
               "Unequal dimensions of each attribute, this should not happen."
        return len(self.time)

generation(start_pose, goal_pose)

Generate the polynomial Curve.

Parameters:

Name Type Description Default
start_pose tuple

Initial pose (x, y, yaw)

required
goal_pose tuple

Target pose (x, y, yaw)

required

Returns:

Name Type Description
traj Traj

The first trajectory that satisfies the acceleration and jerk constraint

Source code in src\python_motion_planning\curve_generation\polynomial_curve.py
Python
def generation(self, start_pose: tuple, goal_pose: tuple):
    """
    Generate the polynomial Curve.

    Parameters:
        start_pose (tuple): Initial pose (x, y, yaw)
        goal_pose (tuple): Target pose (x, y, yaw)

    Returns:
        traj (Traj): The first trajectory that satisfies the acceleration and jerk constraint
    """
    sx, sy, syaw, sv, sa = start_pose
    gx, gy, gyaw, gv, ga = goal_pose

    sv_x = sv * math.cos(syaw)
    sv_y = sv * math.sin(syaw)
    gv_x = gv * math.cos(gyaw)
    gv_y = gv * math.sin(gyaw)

    sa_x = sa * math.cos(syaw)
    sa_y = sa * math.sin(syaw)
    ga_x = ga * math.cos(gyaw)
    ga_y = ga * math.sin(gyaw)

    traj = self.Trajectory()

    for T in np.arange(self.t_min, self.t_max, self.step):
        x_psolver = self.Poly((sx, sv_x, sa_x), (gx, gv_x, ga_x), T)
        y_psolver = self.Poly((sy, sv_y, sa_y), (gy, gv_y, ga_y), T)

        for t in np.arange(0.0, T + self.dt, self.dt):
            traj.time.append(t)
            traj.x.append(x_psolver.x(t))
            traj.y.append(y_psolver.x(t))

            vx = x_psolver.dx(t)
            vy = y_psolver.dx(t)
            traj.v.append(math.hypot(vx, vy))
            traj.yaw.append(math.atan2(vy, vx))

            ax = x_psolver.ddx(t)
            ay = y_psolver.ddx(t)
            a = math.hypot(ax, ay)
            if len(traj.v) >= 2 and traj.v[-1] - traj.v[-2] < 0.0:
                a *= -1
            traj.a.append(a)

            jx = x_psolver.dddx(t)
            jy = y_psolver.dddx(t)
            j = math.hypot(jx, jy)
            if len(traj.a) >= 2 and traj.a[-1] - traj.a[-2] < 0.0:
                j *= -1
            traj.jerk.append(j)

        if max(np.abs(traj.a)) <= self.max_acc and \
           max(np.abs(traj.jerk)) <= self.max_jerk:
            return traj
        else:
            traj.clear()

    return traj

run(points)

Running both generation and animation.

Parameters:

Name Type Description Default
points list[tuple]

path points

required
Source code in src\python_motion_planning\curve_generation\polynomial_curve.py
Python
    def run(self, points: list):
        """
        Running both generation and animation.

        Parameters:
            points (list[tuple]): path points
        """
        assert len(points) >= 2, "Number of points should be at least 2."
        import matplotlib.pyplot as plt

        # generate velocity and acceleration constraints heuristically
        v = [0]
        for i in range(len(points) - 1):
            v.append(1.0)

        a = [(v[i + 1] - v[i]) / 5 for i in range(len(points) - 1)]
        a.append(0)

        # generate curve
        path_x, path_y, path_yaw = [], [], []
        for i in range(len(points) - 1):
            path = self.generation(
				(points[i][0], points[i][1], np.deg2rad(points[i][2]), v[i], a[i]),
				(points[i + 1][0], points[i + 1][1], np.deg2rad(points[i + 1][2]), v[i + 1], a[i + 1])
            )

            for j in range(path.size):
                path_x.append(path.x[j])
                path_y.append(path.y[j])
                path_yaw.append(path.yaw[j])

        # animation
        plt.figure("curve generation")

        # # static
        # plt.plot(path_x, path_y, linewidth=2, c="#1f77b4")
        # for x, y, theta in points:
        #     Plot.plotArrow(x, y, np.deg2rad(theta), 2, 'blueviolet')

        # plt.axis("equal")
        # plt.title(str(self))

        # dynamic
        plt.ion()
        for i in range(len(path_x)):
            plt.clf()
            plt.gcf().canvas.mpl_connect('key_release_event',
                                         lambda event: [exit(0) if event.key == 'escape' else None])
            plt.plot(path_x, path_y, linewidth=2, c="#1f77b4")
            for x, y, theta in points:
                Plot.plotArrow(x, y, np.deg2rad(theta), 2, 'blueviolet')
            Plot.plotCar(path_x[i], path_y[i], path_yaw[i], 1.5, 3, "black")
            plt.axis("equal")
            plt.title(str(self))
            plt.draw()
            plt.pause(0.001)

        plt.show()