-
Notifications
You must be signed in to change notification settings - Fork 4
/
env_setup.py
493 lines (412 loc) · 19.5 KB
/
env_setup.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
from math import acos, cos, sin, ceil, pi
import numpy as np
import os.path
from PIL import Image
from os.path import splitext
from osgeo import gdal
def parseOptions():
'''Parse command line options.
Args:
None
Returns:
(tuple): Tuple containing:
options: Output from optparse -> parseArgs()
args: Output from optparse -> parseArgs()
settings (dict of 'environment'): See DEVELOPMENT.md data structs.
'''
from optparse import OptionParser
# Setup settings dictionary
settings = { "iterations" : 1,
"speed" : 10,
"occupancy" : None,
"ucomponents" : None,
"vcomponents" : None,
"weights" : None,
"weightgrids" : None,
"errors" : None,
"errorgrids" : None,
"start" : None,
"target" : None,
"files" : { "cost2go" : None,
"work2go" : None,
"actiongrid" : None,
"pickle" : None,
"pandas" : None,
"plots" : None,
},
"verbose" : False,
"reuse" : False,
"bounds" : { "upperleft" : None,
"lowerright" : None,
},
}
# Define options
parser = OptionParser()
parser.add_option("-i", "--iterations", dest = "iterations", metavar = "ITERATIONS",
default = 1,
help = "number of solver iterations")
parser.add_option("-l", "--speed", dest = "speed", metavar = "SPEED",
default = 10,
help = "speed of vehicle (cells per second")
parser.add_option("-o", "--occupancy", dest = "occupancy", metavar = "OCCUPANCY",
help = "list of occupancy images (csv)")
parser.add_option("-u", "--ucomponents", dest = "ucomponents", metavar = "UCOMPONENTS",
help = "list of u vector component images (csv)")
parser.add_option("-v", "--vcomponents", dest = "vcomponents", metavar = "VCOMPONENTS",
help = "list of v vector component images (csv)")
parser.add_option("-w", "--weights", dest = "weights", metavar = "WEIGHTS",
help = "list of vector weights (csv)")
parser.add_option("--weightgrids", dest = "weightgrids", metavar = "WEIGHTGRIDS",
help = "list of vector weight grids (csv)")
parser.add_option("-e", "--errors", dest = "errors", metavar = "ERRORS",
help = "list of vector errors (csv)")
parser.add_option("--errorgrids", dest = "errorgrids", metavar = "ERRORGRIDS",
help = "list of vector error grids (csv)")
parser.add_option("-s", "--start", dest = "start", metavar = "START",
help = "start position as row,col")
parser.add_option("-t", "--target", dest = "target", metavar = "TARGET",
help = "target position as row,col")
parser.add_option("-c", "--costfile", dest = "costfile", metavar = "COSTFILE",
help = "file to store grid with traveler action costs for each cell")
parser.add_option("-x", "--workfile", dest = "workfile", metavar = "WORKFILE",
help = "file to store grid with traveler applied work for each cell")
parser.add_option("-a", "--actionfile", dest = "actionfile", metavar = "ACTIONFILE",
help = "file to store grid with traveler actions for each cell")
parser.add_option( "--uactionfile", dest = "uactionfile", metavar = "UACTIONFILE",
help = "file to store grid with environment's applied u-component of force")
parser.add_option( "--vactionfile", dest = "vactionfile", metavar = "VACTIONFILE",
help = "file to store grid with environment's applied v-component of force")
parser.add_option("--picklefile", dest = "picklefile", metavar = "PICKLEFILE",
help = "file to store convergence history as pickle")
parser.add_option("--pandasfile", dest = "pandasfile", metavar = "PANDASFILE",
help = "file to store convergence history as pandas")
parser.add_option("--plotsfiles", dest = "plotsfile", metavar = "PLOTSFILE",
help = "file (prefix) to store convergence history plots. Leave off extention")
parser.add_option("--verbose", dest = "verbose", metavar = "VERBOSE",
action = "store_true", default = False,
help = "Display messages during execution")
parser.add_option("-r", "--reuse", dest = "reuse", metavar = "REUSE",
action = "store_true", default = False,
help = "Reuse existing actiongrid for goto planning")
parser.add_option("-b", "--bounds", dest = "bounds", metavar = "BOUNDS",
help = "Raster boundaries as x1,y1,x2,y2")
# Get options
(options, args) = parser.parse_args()
# Check that required arguments exist
if options.occupancy is None \
or options.start is None \
or options.target is None \
or options.costfile is None \
or options.workfile is None \
or options.actionfile is None:
(options, args, settings) = None, None, None
return (options, args, settings)
settings["files"]["cost2go"] = options.costfile
settings["files"]["work2go"] = options.workfile
settings["files"]["actiongrid"] = options.actionfile
settings["files"]["pickle"] = options.picklefile
settings["files"]["pandas"] = options.pandasfile
settings["files"]["plots"] = options.plotsfile
settings["files"]["uaction"] = options.uactionfile
settings["files"]["vaction"] = options.vactionfile
settings["iterations"] = int(options.iterations)
settings["speed"] = float(options.speed)
settings["occupancy"] = options.occupancy.split(",")
if options.ucomponents is not None:
settings["ucomponents"] = options.ucomponents.split(",")
if options.vcomponents is not None:
settings["vcomponents"] = options.vcomponents.split(",")
if options.weights is not None:
settings["weights"] = [float(w) for w in options.weights .split(",")]
if options.errors is not None:
settings["errors"] = [float(e) for e in options.errors .split(",")]
if options.weightgrids is not None:
settings["weightgrids"] = options.weightgrids.split(",")
if options.errorgrids is not None:
settings["errorgrids"] = options.errorgrids.split(",")
try:
settings["start"] = (int(options.start.split(",")[0]),
int(options.start.split(",")[1]))
settings["target"] = (int(options.target.split(",")[0]),
int(options.target.split(",")[1]))
except:
(options, args, settings) = None, None, None
return (options, args, settings)
# Esnure that all lists related to the vectors are of same length
if settings["ucomponents"] is not None or settings["vcomponents"] is not None:
try:
if len(settings["ucomponents"]) != len(settings["vcomponents"]):
(options, args, settings) = None, None, None
return (options, args, settings)
except:
(options, args, settings) = None, None, None
return (options, args, settings)
# Ensure all files are truly files
fileCheck = True # Initially assume true, until otherwise seen
for o in settings["occupancy"]:
if os.path.isfile(o) == False:
fileCheck = False
if settings["ucomponents"] is not None or settings["vcomponents"] is not None:
for u in settings["ucomponents"]:
if os.path.isfile(u) == False:
fileCheck = False
for v in settings["vcomponents"]:
if os.path.isfile(v) == False:
fileCheck = False
if fileCheck == False:
(options, args, settings) = None, None, None
return (options, args, settings)
# Misc options
settings["verbose"] = options.verbose
settings["reuse"] = options.reuse
# Boundaries
if options.bounds is not None:
bounds = [int(s) for s in options.bounds.split(",")]
settings["bounds"]["upperleft"] = (bounds[0], bounds[1])
settings["bounds"]["lowerright"] = (bounds[2], bounds[3])
if settings["start"][0] < settings["bounds"]["upperleft"][0] or \
settings["start"][0] >= settings["bounds"]["lowerright"][0] or \
settings["start"][1] < settings["bounds"]["upperleft"][1] or \
settings["start"][1] >= settings["bounds"]["lowerright"][1] or \
settings["target"][0] < settings["bounds"]["upperleft"][0] or \
settings["target"][0] >= settings["bounds"]["lowerright"][0] or \
settings["target"][1] < settings["bounds"]["upperleft"][1] or \
settings["target"][1] >= settings["bounds"]["lowerright"][1]:
(options, args, settings) = None, None, None
return (options, args, settings)
return (options, args, settings)
def getTraveler(start, target, speed_cps, travelType):
'''Creates a dict called 'traveler'.
Traveler is a major data struct used in this code and is
described in DEVELOPMENT.md.
Args:
start (tuple): tuple containing:
(int) Row of traveler's start location.
(int) Column of traveler's start location.
target (tuple): tuple containing:
(int) Row of traveler's target location.
(int) Column of traveler's target location.
speed_cps (float): Speed of traveler in grid cells per second.
travelType (str): Selects discrete action space.
Choices are "4way", "8way", "16way".
Returns:
traveler (dict of 'traveler'): See DEVELOPMENT.md data structs.
'''
travelTypes = ["4way", "8way", "16way"]
action2radians = { ">" : 0.0,
"b" : pi / 4.0,
"^" : pi / 2.0,
"a" : pi * 0.75,
"<" : pi,
"c" : pi * 1.25,
"v" : pi * 1.5,
"d" : pi * 1.75,
"m" : (pi / 2.0) + 0.5 * ((pi * 0.75) - (pi / 2.0)),
"n" : (pi / 4.0) + 0.5 * ((pi / 2.0) - (pi / 4.0)),
"o" : (pi * 0.75) + 0.5 * ((pi) - (pi * 0.75)),
"p" : (0.0) + 0.5 * ((pi / 4.00) - (0.0)),
"w" : (pi * 1.25) + 0.5 * ((pi * 1.5) - (pi * 1.25)),
"x" : (pi * 1.5) + 0.5 * ((pi * 1.75) - (pi * 1.5)),
"y" : (pi) + 0.5 * ((pi * 1.25) - (pi)),
"z" : (pi * 1.75) + 0.5 * ((pi * 2) - (pi * 1.75)),
"*" : 0,
"-" : 0,
" " : 0,
}
traveler = { "start" : start,
"target" : target,
"actionspace" : None,
"action2radians" : action2radians,
"speed_cps" : speed_cps,
}
if travelType == travelTypes[0]:
traveler["actionspace"] = ["^", "v", "<", ">"]
elif travelType == travelTypes[1]:
traveler["actionspace"] = ["^", "v", "<", ">",
"a", "b", "c", "d"]
elif travelType == travelTypes[2]:
traveler["actionspace"] = ["^", "v", "<", ">",
"a", "b", "c", "d", "m", "n", "o", "p", "w", "x", "y", "z"]
return traveler
def printEnv(env):
'''Prints formatted contents of environment.
Args:
env (dict of 'environment'): See DEVELOPMENT.md data structs.
Returns:
None
'''
print("------")
print("Start coordinates")
print(" {}".format(env["start"]))
print("Target coordinates")
print(" {}".format(env["target"]))
print("Region images:")
for i in range(len(env["occupancy"])):
print(" {}".format(env["occupancy"][i]))
print("Vector u images:")
if env["ucomponents"] is not None:
for i in range(len(env["ucomponents"])):
print(" Vector {} : {}".format(i, env["ucomponents"][i]))
else:
print("none")
print("Vector v images:")
if env["vcomponents"] is not None:
for i in range(len(env["vcomponents"])):
print(" Vector {} : {}".format(i, env["vcomponents"][i]))
else:
print("none")
#print("Vector weights:")
#for i in range(len(env["weights"])):
# print(" Vector {} : {}".format(i, env["weights"][i]))
#print("Vector errors:")
#for i in range(len(env["errors"])):
# print(" Vector {} : +/- {}".format(i, env["errors"][i]))
print("------")
def getOccupancyGrid(occupancyImageFiles):
'''Creates single 2D numpy array using all input files
such that if any file indicates an occupied cell,
the cell is occupied in the result array.
Args:
occupancyImageFiles (List of str): Paths to occupancy grids.
Returns:
occgrid (array(int, ndim=2)): Binary occupancy grid.
'''
occgrid = None
name, ext = splitext(occupancyImageFiles[0])
if ext == ".txt":
grids = [np.loadtxt(f, dtype = int, delimiter = ",") for f in occupancyImageFiles]
occgrid = np.zeros((grids[0].shape[0], grids[0].shape[1]), dtype = int)
for g in grids:
occgrid = occgrid + g
if ext == ".png":
grids = [np.asarray(Image.open(f)) for f in occupancyImageFiles]
occgrid = np.zeros((grids[0].shape[0], grids[0].shape[1]))
for row in range(len(occgrid)):
for col in range(len(occgrid[0])):
for g in range(len(grids)):
for c in range(4):
if (grids[g][row][col][c] != 255):
occgrid[row][col] = 1
if ext == ".tif":
r = gdal.Open(occupancyImageFiles[0])
occgrid = r.GetRasterBand(1).ReadAsArray()
occgrid = 0 * occgrid
for f in occupancyImageFiles:
r = gdal.Open(f)
for band in range(r.RasterCount):
occgrid = occgrid + r.GetRasterBand(band + 1).ReadAsArray()
return occgrid
def getComponentGrid(componentImageFile, band = 1):
'''Creates single 2D numpy array using a png image or geotiff.
Intended to read values that are components of environment forces.
Args:
componentImageFile (str): Path to file.
band (int): Band to read for multi-band data such as tiff.
Defaults to 1.
Returns:
compgrid (array(int, ndim=2)): vector component grid.
Warnings:
png support is experimental.
even though it accepts band.. code seems to have a constant 1?
'''
compgrid = None
name, ext = splitext(componentImageFile)
#if ext == ".txt":
# compgrid = np.loadtxt(componentImageFile)
# print(compgrid)
if ext == ".png":
# Creates 2D numpy array where the grayscale
# value of the input image files determined
# the proportion of the input maxval at each cell
imgarray = np.array(Image.open(componentImageFile).convert('LA'))
compgrid = np.zeros((imgarray.shape[0], imgarray.shape[1]))
for row in range(len(compgrid)):
for col in range(len(compgrid[0])):
compgrid[row][col] = imgarray[row][col][0] / 255.0
if ext == ".tif":
comp = gdal.Open(componentImageFile)
compgrid = comp.GetRasterBand(1).ReadAsArray()
return compgrid
def getVectorGrids(ucomponentImageFiles, vcomponentImageFiles, occgrid):
'''Get environment forces as grids of u and v components.
Will produce a list of 2D u-component arrays, and another
list of 2D v-component arrays. Lists are ordered such that
the ith grid in both lists correspond to same force.
Args:
ucomponentimagefiles (list of str): Paths to u components.
vcomponentimagefiles (list of str): Paths to v components.
occgrid (array(int, ndim=2)): Binary occupancy grid.
Returns:
ugrids (list of array(float, ndim=2)): u components.
vgrids (list of array(float, ndim=2)): v components.
'''
if ucomponentImageFiles is None or \
vcomponentImageFiles is None:
ugrids = [None]
ugrids[0] = np.zeros(occgrid.shape)
vgrids = [None]
vgrids[0] = np.zeros(occgrid.shape)
else:
numGrids = len(ucomponentImageFiles)
ugrids = [None for u in range(numGrids)]
vgrids = [None for v in range(numGrids)]
for i in range(numGrids):
ugrids[i] = getComponentGrid(ucomponentImageFiles[i])
vgrids[i] = getComponentGrid(vcomponentImageFiles[i])
return ugrids, vgrids
def getWeightGrids(occgrid, weights, weightgridFiles, numGrids):
'''Weights indicate the relative significance of an environmental
force on the traveler. Weights may be spatially variable,
so weight grids are a weight at each region cell. Can also assign
a constant weight to be placed in each cell.
Args:
occgrid (array(int, ndim=2)): Binary occupancy grid.
weights (List of float): constant weights for each force.
weightgridFiles (List of str): Paths to variable weights for each force.
numGrids (int): Number of grids to generate.
Returns:
weightGrids (list of array(float, ndim=2)): weight grids for each force.
'''
weightgrids = [None for w in range(numGrids)]
# Priority given to weightgrid files
if weightgridFiles is not None:
for i in range(numGrids):
weightgrids[i] = np.loadtxt(weightgridFiles[i])
return weightgrids
# Then to a single value: assigned to all cells
if weights is not None:
for i in range(numGrids):
weightgrids[i] = np.ones(occgrid.shape) * weights[i]
return weightgrids
# Else weight is one
for i in range(numGrids):
weightgrids[i] = np.ones(occgrid.shape)
return weightgrids
def getErrorGrids(occgrid, errors, errorgridFiles, numGrids):
'''Error indicate the uncertainty of an environmental force.
Errors may be spatially variable, so error grids are an
error at each region cell. Can also assign
a constant error to be placed in each cell.
Args:
occgrid (array(int, ndim=2)): Binary occupancy grid.
errors (List of float): constant errors for each force.
errorgridFiles (List of str): Paths to variable errors for each force.
numGrids (int): Number of grids to generate.
Returns:
errorGrids (list of array(float, ndim=2)): error grids for each force.
'''
errorgrids = [None for w in range(numGrids)]
# Priority given to errorgrid files
if errorgridFiles is not None:
for i in range(numGrids):
errorgrids[i] = np.loadtxt(errorgridFiles[i])
return errorgrids
# Then to a single value: assigned to all cells
if errors is not None:
for i in range(numGrids):
errorgrids[i] = np.ones(occgrid.shape) * errors[i]
return errorgrids
# Else error is one
for i in range(numGrids):
errorgrids[i] = np.zeros(occgrid.shape)
return errorgrids