Skip to content

Commit a68f550

Browse files
committed
Merge branch 'master' of github.com:ARSControl/cnn_coverage
2 parents 7681b9e + e550054 commit a68f550

File tree

3 files changed

+1047
-0
lines changed

3 files changed

+1047
-0
lines changed

scripts/l-shaped.py

Lines changed: 339 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,339 @@
1+
import numpy as np
2+
from scipy.spatial import Voronoi, voronoi_plot_2d
3+
import matplotlib.pyplot as plt
4+
import random
5+
from shapely import Polygon, Point, intersection
6+
from tqdm import tqdm
7+
from pathlib import Path
8+
import math
9+
from scipy.optimize import minimize
10+
import argparse
11+
12+
from copy import deepcopy as dc
13+
from sklearn.mixture import GaussianMixture
14+
15+
from utils import *
16+
from models import *
17+
18+
19+
def parse_args():
20+
parser = argparse.ArgumentParser()
21+
parser.add_argument('--robots-num', type=int, default=12, help="number of robots")
22+
parser.add_argument('--range', type=float, default=3.0, help="sensing range")
23+
parser.add_argument('--width', type=float, default=30.0, help="area width")
24+
args = parser.parse_args()
25+
return args
26+
27+
args = parse_args()
28+
29+
ROBOTS_NUM = args.robots_num
30+
ROBOT_RANGE = args.range
31+
TARGETS_NUM = 2
32+
COMPONENTS_NUM = 4
33+
PARTICLES_NUM = 500
34+
AREA_W = args.width
35+
GRID_STEPS = 64
36+
vmax = 1.5
37+
SAFETY_DIST = 2.0
38+
EPISODES = 1
39+
NUM_OBSTACLES = 1
40+
NUM_STEPS = 500
41+
NUM_CHANNELS = 3
42+
GAMMA = 0.5
43+
USE_CBF = False
44+
SAVE_POS = False
45+
46+
resolution = 2 * ROBOT_RANGE / GRID_STEPS
47+
48+
49+
import numpy as np
50+
import matplotlib.pyplot as plt
51+
from pathlib import Path
52+
53+
path = Path().resolve()
54+
path = path / "trained_models"
55+
# MODEL_PATH = path/'multichannel_2d_cnn.pt'
56+
MODEL_PATH = path/'2d_cnn_3ch_cbf.pt'
57+
print("Model Path ", MODEL_PATH)
58+
59+
60+
import torch
61+
from torch import nn
62+
from torch.nn import functional as F
63+
64+
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
65+
print("Device: ", device)
66+
67+
model = Multichannel_2D_CNN(NUM_CHANNELS).to(device)
68+
69+
model.load_state_dict(torch.load(MODEL_PATH, map_location=device))
70+
model.eval()
71+
print(model)
72+
73+
eval_data = np.zeros((EPISODES, NUM_STEPS))
74+
collision_counter = np.zeros((EPISODES, NUM_STEPS))
75+
76+
def objective_function(u):
77+
return np.linalg.norm(u)**2
78+
79+
def safety_constraint(u, A, b):
80+
return -np.dot(A,u) + b
81+
82+
for episode in range(EPISODES):
83+
print(f"*** Episode {episode} ***")
84+
targets = np.zeros((TARGETS_NUM, 1, 2))
85+
targets[0, 0, 0] = 0.0
86+
targets[0, 0, 1] = 12.5
87+
targets[1, 0, 0] = 8.0
88+
targets[1, 0, 1] = 7.5
89+
# for i in range(TARGETS_NUM):
90+
# targets[i, 0, 0] = -0.5*(AREA_W-1) + (AREA_W-1) * np.random.rand()
91+
# targets[i, 0, 1] = -0.5*(AREA_W-1) + (AREA_W-1) * np.random.rand()
92+
93+
# plt.plot([-0.5*AREA_W, 0.5*AREA_W], [-0.5*AREA_W, -0.5*AREA_W], c='tab:blue', label="Environment")
94+
# plt.plot([0.5*AREA_W, 0.5*AREA_W], [-0.5*AREA_W, 0.5*AREA_W], c='tab:blue')
95+
# plt.plot([0.5*AREA_W, -0.5*AREA_W], [0.5*AREA_W, 0.5*AREA_W], c='tab:blue')
96+
# plt.plot([-0.5*AREA_W, -0.5*AREA_W], [0.5*AREA_W, -0.5*AREA_W], c='tab:blue')
97+
# plt.scatter(targets[:, :, 0], targets[:, :, 1], c='tab:orange', label="Targets")
98+
# # plt.legend()
99+
# plt.show()
100+
101+
STD_DEV = 3.0
102+
samples = np.zeros((TARGETS_NUM, PARTICLES_NUM, 2))
103+
for k in range(TARGETS_NUM):
104+
for i in range(PARTICLES_NUM):
105+
samples[k, i, :] = targets[k, 0, :] + STD_DEV * np.random.randn(1, 2)
106+
107+
108+
109+
# Fit GMM
110+
samples = samples.reshape((TARGETS_NUM*PARTICLES_NUM, 2))
111+
print(samples.shape)
112+
gmm = GaussianMixture(n_components=COMPONENTS_NUM, covariance_type='full', max_iter=1000)
113+
gmm.fit(samples)
114+
115+
means = gmm.means_
116+
covariances = gmm.covariances_
117+
mix = gmm.weights_
118+
119+
# print(f"Means: {means}")
120+
# print(f"Covs: {covariances}")
121+
# print(f"Mix: {mix}")
122+
123+
124+
## -------- Generate decentralized probability grid ---------
125+
GRID_STEPS = 64
126+
s = AREA_W/GRID_STEPS # step
127+
128+
xg = np.linspace(-0.5*AREA_W, 0.5*AREA_W, GRID_STEPS)
129+
yg = np.linspace(-0.5*AREA_W, 0.5*AREA_W, GRID_STEPS)
130+
Xg, Yg = np.meshgrid(xg, yg)
131+
# Xg.shape
132+
# print(Xg.shape)
133+
134+
Z = gmm_pdf(Xg, Yg, means, covariances, mix)
135+
Z = Z.reshape(GRID_STEPS, GRID_STEPS)
136+
Zmax = np.max(Z)
137+
Z = Z / Zmax
138+
139+
obstacles = np.array([5.0, -5.0])
140+
OBS_W = 20.0
141+
142+
fig, ax = plt.subplots(1, 1, figsize=(8,8))
143+
plot_occgrid(Xg, Yg, Z, ax=ax)
144+
ax.plot([obstacles[0]-0.5*OBS_W, obstacles[0]+0.5*OBS_W], [obstacles[1]-0.5*OBS_W, obstacles[1]-0.5*OBS_W], c='tab:blue')
145+
ax.plot([obstacles[0]+0.5*OBS_W, obstacles[0]+0.5*OBS_W], [obstacles[1]-0.5*OBS_W, obstacles[1]+0.5*OBS_W], c='tab:blue')
146+
ax.plot([obstacles[0]+0.5*OBS_W, obstacles[0]-0.5*OBS_W], [obstacles[1]+0.5*OBS_W, obstacles[1]+0.5*OBS_W], c='tab:blue')
147+
ax.plot([obstacles[0]-0.5*OBS_W, obstacles[0]-0.5*OBS_W], [obstacles[1]+0.5*OBS_W, obstacles[1]-0.5*OBS_W], c='tab:blue')
148+
# ---------- Simulate episode ---------
149+
# ROBOTS_NUM = np.random.randint(6, ROBOTS_MAX)
150+
converged = False
151+
points = np.zeros((ROBOTS_NUM, 2))
152+
points[:, 0] = -0.5*AREA_W + 8 * np.random.rand(ROBOTS_NUM)
153+
points[:, 1] = -0.5*AREA_W + AREA_W * np.random.rand(ROBOTS_NUM)
154+
robots_hist = np.zeros((1, points.shape[0], points.shape[1]))
155+
robots_hist[0, :, :] = points
156+
vis_regions = []
157+
discretize_precision = 0.5
158+
dt = 0.25
159+
failed = False
160+
161+
162+
163+
164+
imgs = np.zeros((1, ROBOTS_NUM, NUM_CHANNELS, GRID_STEPS, GRID_STEPS))
165+
vels = np.zeros((1, ROBOTS_NUM, 2))
166+
167+
r_step = 2 * ROBOT_RANGE / GRID_STEPS
168+
denom = np.sum(s**2 * gmm_pdf(Xg, Yg, means, covariances, mix))
169+
print("Total info: ", denom)
170+
for s in range(1, NUM_STEPS+1):
171+
# print(f"*** Step {s} ***")
172+
all_stopped = True
173+
174+
# mirror points across each edge of the env
175+
dummy_points = np.zeros((5*ROBOTS_NUM, 2))
176+
dummy_points[:ROBOTS_NUM, :] = points
177+
mirrored_points = mirror(points, AREA_W)
178+
mir_pts = np.array(mirrored_points)
179+
dummy_points[ROBOTS_NUM:, :] = mir_pts
180+
181+
# Voronoi partitioning
182+
vor = Voronoi(dummy_points)
183+
184+
conv = True
185+
lim_regions = []
186+
img_s = np.zeros((ROBOTS_NUM, NUM_CHANNELS, GRID_STEPS, GRID_STEPS))
187+
vel_s = np.zeros((ROBOTS_NUM, 2))
188+
num = 0.0
189+
collision = False
190+
for idx in range(ROBOTS_NUM):
191+
p_i = vor.points[idx]
192+
193+
194+
# Save grid
195+
xg_i = np.linspace(-ROBOT_RANGE, ROBOT_RANGE, GRID_STEPS)
196+
yg_i = np.linspace(-ROBOT_RANGE, ROBOT_RANGE, GRID_STEPS)
197+
Xg_i, Yg_i = np.meshgrid(xg_i, yg_i)
198+
Z_i = gmm_pdf(Xg_i, Yg_i, means-p_i, covariances, mix)
199+
Z_i = Z_i.reshape(GRID_STEPS, GRID_STEPS)
200+
Zmax_i = np.max(Z_i)
201+
Z_i = Z_i / Zmax_i
202+
203+
img_i = Z_i * 255
204+
img_i = np.expand_dims(img_i, 0)
205+
206+
neighs = np.delete(vor.points[:ROBOTS_NUM], idx, 0)
207+
local_pts = neighs - p_i
208+
209+
# Remove undetected neighbors
210+
undetected = []
211+
for i in range(local_pts.shape[0]):
212+
if local_pts[i, 0] < -ROBOT_RANGE or local_pts[i, 0] > ROBOT_RANGE or local_pts[i, 1] < -ROBOT_RANGE or local_pts[i, 1] > ROBOT_RANGE:
213+
undetected.append(i)
214+
215+
local_pts = np.delete(local_pts, undetected, 0)
216+
217+
img_obs = np.zeros((1, GRID_STEPS, GRID_STEPS))
218+
img_neighs = np.zeros((1, GRID_STEPS, GRID_STEPS))
219+
for i in range(GRID_STEPS):
220+
for j in range(GRID_STEPS):
221+
# jj = GRID_STEPS-1-j
222+
p_ij = np.array([-ROBOT_RANGE+j*r_step, -ROBOT_RANGE+i*r_step])
223+
# print(f"Point ({i},{j}): {p_ij}")
224+
for n in local_pts:
225+
if np.linalg.norm(n - p_ij) <= SAFETY_DIST:
226+
img_neighs[0, i, j] = 255
227+
228+
# Check if outside boundaries
229+
p_w = p_ij + p_i
230+
if p_w[0] < -0.5*AREA_W or p_w[0] > 0.5*AREA_W or p_w[1] < -0.5*AREA_W or p_w[1] > 0.5*AREA_W:
231+
img_obs[0, i, j] = 255
232+
233+
# Check for obstacles
234+
if p_w[0] > obstacles[0] - 0.6*OBS_W and p_w[0] < obstacles[0] + 0.6*OBS_W and p_w[1] > obstacles[1] - 0.6*OBS_W and p_w[1] < obstacles[1] + 0.6*OBS_W:
235+
# if np.linalg.norm(p_w - obs) < SAFETY_DIST:
236+
img_obs[0, i, j] = 255
237+
238+
# -------- EVAL -------
239+
region_id = vor.point_region[idx]
240+
cell = vor.regions[region_id]
241+
verts = vor.vertices[cell]
242+
poly = Polygon(verts)
243+
th = np.arange(0, 2*np.pi+np.pi/20, np.pi/20)
244+
rng_pts = p_i + ROBOT_RANGE * np.array([np.cos(th), np.sin(th)]).transpose()
245+
range_poly = Polygon(rng_pts)
246+
lim_region = intersection(poly, range_poly)
247+
248+
dA = r_step**2
249+
for x_i in np.arange(p_i[0]-ROBOT_RANGE, p_i[0]+ROBOT_RANGE, r_step):
250+
for y_i in np.arange(p_i[1]-ROBOT_RANGE, p_i[1]+ROBOT_RANGE, r_step):
251+
pt = Point(x_i, y_i)
252+
if lim_region.contains(pt):
253+
dA_pdf = dA * gmm_pdf(x_i, y_i, means, covariances, mix)
254+
num += dA_pdf
255+
256+
# Check collisions
257+
dist = np.linalg.norm(local_pts, axis=1)
258+
if (dist < 0.1).any():
259+
collision_counter[episode, s-1] = 1
260+
print("Collision detected with neighbors!")
261+
if p_i[0] > obstacles[0] - 0.5*OBS_W and p_i[0] < obstacles[0] + 0.5*OBS_W and p_i[1] > obstacles[1] - 0.5*OBS_W and p_i[1] < obstacles[1] + 0.5*OBS_W:
262+
# if np.linalg.norm(obs - p_i) < 0.5*SAFETY_DIST:
263+
collision_counter[episode, s-1] = 1
264+
print("Collision detected with obstacle!")
265+
failed = True
266+
267+
268+
img_i = np.concatenate((img_i, img_neighs), 0)
269+
img_i = np.concatenate((img_i, img_obs), 0)
270+
# print("img_i shape: ", img_i.shape)
271+
272+
img_in = torch.from_numpy(img_i).unsqueeze(0)#.unsqueeze(0)
273+
img_in = img_in.to(torch.float).to(device)
274+
vel_i = model(img_in) * resolution
275+
vel_i = vel_i.squeeze(0)
276+
vel_i = vel_i.cpu().detach().numpy()
277+
278+
# CBF
279+
if USE_CBF:
280+
local_pts = neighs - p_i
281+
constraints = []
282+
for n in local_pts:
283+
h = np.linalg.norm(n)**2 - SAFETY_DIST**2
284+
A_cbf = 2*n
285+
b_cbf = GAMMA * h
286+
constraints.append({'type': 'ineq', 'fun': lambda u: safety_constraint(u, A_cbf, b_cbf)})
287+
288+
local_obs = obstacles - p_i
289+
for obs in local_obs:
290+
h = np.linalg.norm(obs)**2 - (2*SAFETY_DIST)**2
291+
A_cbf = 2*obs
292+
b_cbf = GAMMA * h
293+
constraints.append({'type': 'ineq', 'fun': lambda u: safety_constraint(u, A_cbf, b_cbf)})
294+
# print("vdes: ", vel_i)
295+
# print("Acbf: ", A_cbf)
296+
# print("b_cbf: ", b_cbf)
297+
# print("h: ", h)
298+
obj = lambda u: objective_function(u-vel_i)
299+
res = minimize(obj, vel_i, constraints=constraints, bounds=[(-vmax, vmax), (-vmax, vmax)])
300+
vel_i = res.x
301+
302+
# print(f"Velocity of robot {idx}: {vel_i}")
303+
# print("points[idx] shape: ", points[idx, :].shape)
304+
points[idx, 0] = points[idx, 0] + vel_i[0]*dt
305+
points[idx, 1] = points[idx, 1] + vel_i[1]*dt
306+
307+
if np.linalg.norm(vel_i) > 0.15:
308+
all_stopped = False
309+
310+
robots_hist = np.concatenate((robots_hist, np.expand_dims(points, 0)))
311+
eta = num / denom
312+
# print("Efficiency: ", eta)
313+
eval_data[episode, s-1] = eta[0]
314+
315+
316+
317+
if all_stopped:
318+
break
319+
320+
path = Path().resolve()
321+
res_path = path / "results"
322+
323+
if SAVE_POS:
324+
np.save(res_path/"eta16.npy", eval_data)
325+
np.save(res_path/"collisions16.npy", collision_counter)
326+
327+
if not failed:
328+
np.save(res_path/"elle_pos.npy", robots_hist)
329+
330+
331+
for i in range(ROBOTS_NUM):
332+
ax.plot(robots_hist[:, i, 0], robots_hist[:, i, 1])
333+
ax.scatter(robots_hist[-1, i, 0], robots_hist[-1, i, 1])
334+
335+
336+
plt.show()
337+
338+
339+

0 commit comments

Comments
 (0)