Add obstacle class and function add_obstacles to add obstacles to path.
Restructure class ReferencePath a bit. Add safety_margin to update_bounds function.master
parent
7940964b29
commit
6ec93febd3
|
@ -3,6 +3,13 @@ import math
|
||||||
from map import Map
|
from map import Map
|
||||||
from bresenham import bresenham
|
from bresenham import bresenham
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
|
import matplotlib.patches as plt_patches
|
||||||
|
from scipy.signal import savgol_filter
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
DRIVABLE_AREA = '#BDC3C7'
|
||||||
|
WAYPOINTS = '#D0D3D4'
|
||||||
|
OBSTACLE = '#2E4053'
|
||||||
|
|
||||||
|
|
||||||
############
|
############
|
||||||
|
@ -41,13 +48,43 @@ class Waypoint:
|
||||||
return ((self.x - other.x)**2 + (self.y - other.y)**2)**0.5
|
return ((self.x - other.x)**2 + (self.y - other.y)**2)**0.5
|
||||||
|
|
||||||
|
|
||||||
|
############
|
||||||
|
# Obstacle #
|
||||||
|
############
|
||||||
|
|
||||||
|
class Obstacle:
|
||||||
|
def __init__(self, cx, cy, radius):
|
||||||
|
"""
|
||||||
|
Constructor for a circular obstacle to be place on a path.
|
||||||
|
:param cx: x coordinate of center of obstacle in world coordinates
|
||||||
|
:param cy: y coordinate of center of obstacle in world coordinates
|
||||||
|
:param radius: radius of circular obstacle in m
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.cx = cx
|
||||||
|
self.cy = cy
|
||||||
|
self.radius = radius
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
"""
|
||||||
|
Display obstacle.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Draw circle
|
||||||
|
circle = plt_patches.Circle(xy=(self.cx, self.cy), radius=
|
||||||
|
self.radius, color=OBSTACLE)
|
||||||
|
ax = plt.gca()
|
||||||
|
ax.add_patch(circle)
|
||||||
|
|
||||||
|
|
||||||
##################
|
##################
|
||||||
# Reference Path #
|
# Reference Path #
|
||||||
##################
|
##################
|
||||||
|
|
||||||
|
|
||||||
class ReferencePath:
|
class ReferencePath:
|
||||||
def __init__(self, map, wp_x, wp_y, resolution, smoothing_distance, max_width):
|
def __init__(self, map, wp_x, wp_y, resolution, smoothing_distance,
|
||||||
|
max_width):
|
||||||
"""
|
"""
|
||||||
Reference Path object. Create a reference trajectory from specified
|
Reference Path object. Create a reference trajectory from specified
|
||||||
corner points with given resolution. Smoothing around corners can be
|
corner points with given resolution. Smoothing around corners can be
|
||||||
|
@ -80,6 +117,9 @@ class ReferencePath:
|
||||||
# Compute path width (attribute of each waypoint)
|
# Compute path width (attribute of each waypoint)
|
||||||
self._compute_width(max_width=max_width)
|
self._compute_width(max_width=max_width)
|
||||||
|
|
||||||
|
# Obstacles on path
|
||||||
|
self.obstacles = list()
|
||||||
|
|
||||||
def _construct_path(self, wp_x, wp_y):
|
def _construct_path(self, wp_x, wp_y):
|
||||||
"""
|
"""
|
||||||
Construct path from given waypoints.
|
Construct path from given waypoints.
|
||||||
|
@ -250,13 +290,17 @@ class ReferencePath:
|
||||||
|
|
||||||
return min_width, min_cell
|
return min_width, min_cell
|
||||||
|
|
||||||
def update_bounds(self, wp_id):
|
def update_bounds(self, car, wp_id):
|
||||||
"""
|
"""
|
||||||
Compute upper and lower bounds of the drivable area orthogonal to
|
Compute upper and lower bounds of the drivable area orthogonal to
|
||||||
the given waypoint.
|
the given waypoint.
|
||||||
|
:param car: car model driving on the path
|
||||||
:param wp_id: ID of reference waypoint
|
:param wp_id: ID of reference waypoint
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# Get car-dependent safety margin
|
||||||
|
safety_margin = car.safety_margin
|
||||||
|
|
||||||
# Get reference waypoint
|
# Get reference waypoint
|
||||||
wp = self.waypoints[wp_id]
|
wp = self.waypoints[wp_id]
|
||||||
|
|
||||||
|
@ -311,6 +355,10 @@ class ReferencePath:
|
||||||
ub = sign_ub * np.sqrt((ub_ls[0]-wp.x)**2+(ub_ls[1]-wp.y)**2)
|
ub = sign_ub * np.sqrt((ub_ls[0]-wp.x)**2+(ub_ls[1]-wp.y)**2)
|
||||||
lb = sign_lb * np.sqrt((lb_ls[0]-wp.x)**2+(lb_ls[1]-wp.y)**2)
|
lb = sign_lb * np.sqrt((lb_ls[0]-wp.x)**2+(lb_ls[1]-wp.y)**2)
|
||||||
|
|
||||||
|
# Add safety margin to bounds
|
||||||
|
ub = ub - (safety_margin[1] + 2 * self.map.resolution)
|
||||||
|
lb = lb + (safety_margin[1] + 2 * self.map.resolution)
|
||||||
|
|
||||||
# Update member variables of waypoint
|
# Update member variables of waypoint
|
||||||
wp.ub = ub
|
wp.ub = ub
|
||||||
wp.lb = lb
|
wp.lb = lb
|
||||||
|
@ -318,6 +366,24 @@ class ReferencePath:
|
||||||
|
|
||||||
return lb, ub
|
return lb, ub
|
||||||
|
|
||||||
|
def add_obstacles(self, obstacles):
|
||||||
|
"""
|
||||||
|
Add obstacles to the path.
|
||||||
|
:param obstacles: list of obstacle objects
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Extend list of obstacles
|
||||||
|
self.obstacles.extend(obstacles)
|
||||||
|
|
||||||
|
# Iterate over list of obstacles
|
||||||
|
for obstacle in obstacles:
|
||||||
|
radius_px = int(np.ceil(obstacle.radius / self.map.resolution))
|
||||||
|
cx_px, cy_px = self.map.w2m(obstacle.cx, obstacle.cy)
|
||||||
|
y, x = np.ogrid[-radius_px: radius_px, -radius_px: radius_px]
|
||||||
|
index = x ** 2 + y ** 2 <= radius_px ** 2
|
||||||
|
self.map.data[cy_px-radius_px:cy_px+radius_px, cx_px-radius_px:
|
||||||
|
cx_px+radius_px][index] = 0
|
||||||
|
|
||||||
def show(self, display_drivable_area=True):
|
def show(self, display_drivable_area=True):
|
||||||
"""
|
"""
|
||||||
Display path object on current figure.
|
Display path object on current figure.
|
||||||
|
@ -328,12 +394,18 @@ class ReferencePath:
|
||||||
# Clear figure
|
# Clear figure
|
||||||
plt.clf()
|
plt.clf()
|
||||||
|
|
||||||
|
# Disabled ticks
|
||||||
|
plt.xticks([])
|
||||||
|
plt.yticks([])
|
||||||
|
|
||||||
# Plot map in gray-scale and set extent to match world coordinates
|
# Plot map in gray-scale and set extent to match world coordinates
|
||||||
plt.imshow(np.flipud(self.map.data), cmap='gray',
|
canvas = np.ones(self.map.data.shape)
|
||||||
|
plt.imshow(canvas, cmap='gray',
|
||||||
extent=[self.map.origin[0], self.map.origin[0] +
|
extent=[self.map.origin[0], self.map.origin[0] +
|
||||||
self.map.width * self.map.resolution,
|
self.map.width * self.map.resolution,
|
||||||
self.map.origin[1], self.map.origin[1] +
|
self.map.origin[1], self.map.origin[1] +
|
||||||
self.map.height * self.map.resolution])
|
self.map.height * self.map.resolution], vmin=0.0,
|
||||||
|
vmax=1.0)
|
||||||
|
|
||||||
# Get x and y coordinates for all waypoints
|
# Get x and y coordinates for all waypoints
|
||||||
wp_x = np.array([wp.x for wp in self.waypoints])
|
wp_x = np.array([wp.x for wp in self.waypoints])
|
||||||
|
@ -346,22 +418,56 @@ class ReferencePath:
|
||||||
wp_lb_y = np.array([wp.border_cells[1][1] for wp in self.waypoints])
|
wp_lb_y = np.array([wp.border_cells[1][1] for wp in self.waypoints])
|
||||||
|
|
||||||
# Plot waypoints
|
# Plot waypoints
|
||||||
plt.scatter(wp_x, wp_y, color='#99A3A4', s=3)
|
plt.scatter(wp_x, wp_y, color=WAYPOINTS, s=3)
|
||||||
|
|
||||||
# Plot arrows indicating drivable area
|
# Plot arrows indicating drivable area
|
||||||
if display_drivable_area:
|
if display_drivable_area:
|
||||||
plt.quiver(wp_x, wp_y, wp_ub_x - wp_x, wp_ub_y - wp_y, scale=1,
|
plt.quiver(wp_x, wp_y, wp_ub_x - wp_x, wp_ub_y - wp_y, scale=1,
|
||||||
units='xy', width=1.5*self.resolution, color='#2ECC71',
|
units='xy', width=0.2*self.resolution, color=DRIVABLE_AREA,
|
||||||
headwidth=1, headlength=0)
|
headwidth=1, headlength=0)
|
||||||
plt.quiver(wp_x, wp_y, wp_lb_x - wp_x, wp_lb_y - wp_y, scale=1,
|
plt.quiver(wp_x, wp_y, wp_lb_x - wp_x, wp_lb_y - wp_y, scale=1,
|
||||||
units='xy', width=1.5*self.resolution, color='#2ECC71',
|
units='xy', width=0.2*self.resolution, color=DRIVABLE_AREA,
|
||||||
headwidth=1, headlength=0)
|
headwidth=1, headlength=0)
|
||||||
|
|
||||||
|
# Plot border of path
|
||||||
|
bl_x = np.array([wp.border_cells[0][0] for wp in
|
||||||
|
self.waypoints] +
|
||||||
|
[self.waypoints[0].border_cells[0][0]])
|
||||||
|
bl_y = np.array([wp.border_cells[0][1] for wp in
|
||||||
|
self.waypoints] +
|
||||||
|
[self.waypoints[0].border_cells[0][1]])
|
||||||
|
br_x = np.array([wp.border_cells[1][0] for wp in
|
||||||
|
self.waypoints] +
|
||||||
|
[self.waypoints[0].border_cells[1][0]])
|
||||||
|
br_y = np.array([wp.border_cells[1][1] for wp in
|
||||||
|
self.waypoints] +
|
||||||
|
[self.waypoints[0].border_cells[1][1]])
|
||||||
|
# Smooth border
|
||||||
|
# bl_x = savgol_filter(bl_x, 15, 9)
|
||||||
|
# bl_y = savgol_filter(bl_y, 15, 9)
|
||||||
|
# br_x = savgol_filter(br_x, 15, 9)
|
||||||
|
# br_y = savgol_filter(br_y, 15, 9)
|
||||||
|
|
||||||
|
# If circular path, connect start and end point
|
||||||
|
if np.abs(self.waypoints[-1] - self.waypoints[0]) <= 2*self.resolution:
|
||||||
|
plt.plot(bl_x, bl_y, color=OBSTACLE)
|
||||||
|
plt.plot(br_x, br_y, color=OBSTACLE)
|
||||||
|
# If not circular, close path at start and end
|
||||||
|
else:
|
||||||
|
plt.plot(bl_x[:-1], bl_y[:-1], color=OBSTACLE)
|
||||||
|
plt.plot(br_x[:-1], br_y[:-1], color=OBSTACLE)
|
||||||
|
plt.plot((bl_x[-2],br_x[-2]), (bl_y[-2], br_y[-2]), color=OBSTACLE)
|
||||||
|
plt.plot((bl_x[0],br_x[0]), (bl_y[0], br_y[0]), color=OBSTACLE)
|
||||||
|
|
||||||
|
# Plot obstacles
|
||||||
|
for obstacle in self.obstacles:
|
||||||
|
obstacle.show()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
||||||
# Select Path | 'Race' or 'Q'
|
# Select Path | 'Race' or 'Q'
|
||||||
path = 'Q'
|
path = 'Race'
|
||||||
|
|
||||||
# Create Map
|
# Create Map
|
||||||
if path == 'Race':
|
if path == 'Race':
|
||||||
|
@ -388,6 +494,17 @@ if __name__ == '__main__':
|
||||||
print('Invalid path!')
|
print('Invalid path!')
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
|
obs1 = Obstacle(cx=0.0, cy=0.0, radius=0.05)
|
||||||
|
obs2 = Obstacle(cx=-0.8, cy=-0.5, radius=0.05)
|
||||||
|
obs3 = Obstacle(cx=-0.7, cy=-1.5, radius=0.05)
|
||||||
|
obs4 = Obstacle(cx=-0.3, cy=-1.0, radius=0.05)
|
||||||
|
obs5 = Obstacle(cx=0.3, cy=-1.0, radius=0.05)
|
||||||
|
obs6 = Obstacle(cx=0.75, cy=-1.5, radius=0.05)
|
||||||
|
obs7 = Obstacle(cx=0.7, cy=-0.9, radius=0.05)
|
||||||
|
obs8 = Obstacle(cx=1.2, cy=0.0, radius=0.05)
|
||||||
|
reference_path.add_obstacles([obs1, obs2, obs3, obs4, obs5, obs6, obs7,
|
||||||
|
obs8])
|
||||||
|
|
||||||
reference_path.show()
|
reference_path.show()
|
||||||
plt.show()
|
plt.show()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue