19
19
import cPickle as pickle
25
logger = log.getLogger('mapper')
26
logger.setLevel(logging.INFO)
27
USE_BACKTRACING = True
29
24
_map_resolution_levels = 4 # number of different levels of resolution
31
26
def loadmap(fname):
32
27
return pickle.load(open(fname))
34
class MapBuilder(object):
35
def __init__(self, resolution,
37
levels=_map_resolution_levels,
40
self.vmap = VoxelMap(resolution, levels=levels,
42
self.odometry_map = odometry_map
44
self.bestpose = poseutil.Pose3D()
49
def add_pointcloud(self, pc):
50
if self.scancount == 0:
52
self.vmap.add_scan(pc, pc.pose)
53
pc.pose = (poseutil.Pose3D() if pc.pose is None else pc.pose)
54
return self.vmap.get_voxels(level=-1), pc.pose
56
# reduce number of scan points to one per voxel
57
# sample = pc.volumetricsample(self.vmap.resolution)
59
logger.info('--------- Scan %d ---------' % self.scancount)
63
guessPose = self.bestpose
68
if not self.odometry_map:
69
self.bestpose, maxcost = self.vmap.align_points(pc, guessPose)
71
alignment_time = time.time() - start
74
if self.odometry_map: # create map without aligning
75
self.vmap.add_scan(pc, guessPose)
77
self.vmap.add_scan(pc, self.bestpose)
79
mapupdate_time = time.time() - start
80
logger.info('Map voxel count:{0}'.format( len(self.vmap)))
82
logger.info('Aligned pose:{0}'.format(self.bestpose))
83
logger.info('Cost value: {0}'.format(maxcost))
85
logger.info('Alignment time(s): %.2f' % alignment_time)
86
logger.info('Map update time(s): %.2f' % mapupdate_time)
87
return self.vmap.get_voxels(level=-1), self.bestpose
89
29
def buildmap(pointclouds, resolution, visualise=True, odometry_map=False,
91
30
bestpose_queue=None,
93
32
'''Takes an iterable of point clouds which can have guess poses and produce
133
75
if not odometry_map:
134
76
bestpose, maxcost = vmap.align_points(sample, guessPose)
135
77
if bestpose_queue:
136
logger.info("Best pose {0}".format(bestpose))
137
78
bestpose_queue.put_force(bestpose)
139
80
alignment_time = time.time() - start
141
82
start = time.time()
142
83
if odometry_map: # create map without aligning
143
vmap.add_scan(pc, guessPose)
84
vmap.add_points(pc, guessPose)
145
vmap.add_scan(pc, bestpose)
86
vmap.add_points(pc, bestpose)
148
89
(vdata, vres) = vmap.get_voxels(level=-1)
149
90
voxel_queue.put_force((vdata, vres))
151
92
mapupdate_time = time.time() - start
152
logger.info('Map voxel count:{0}'.format( len(vmap)))
154
logger.info('Aligned pose:{0}'.format(bestpose))
155
logger.info('Cost value: {0}'.format(maxcost))
157
#logger.info('Execution times (s): %.2f\t%.2f' % (alignment_time, mapupdate_time))
158
logger.info('Alignment time(s): %.2f' % alignment_time)
159
logger.info('Map update time(s): %.2f' % mapupdate_time)
93
print 'Map voxel count:', len(vmap)
96
print 'Aligned pose:', bestpose
97
print 'Cost value: ', maxcost
100
#print 'Execution times (s): %.2f\t%.2f' % (alignment_time, mapupdate_time)
101
print 'Alignment time(s): %.2f' % alignment_time
102
print 'Map update time(s): %.2f' % mapupdate_time
162
105
def _build_Kinect_sensor_model(resolution, minR=0.5, maxR=5):
260
203
self._rv = None # remove visualiser
261
204
pickle.dump(self, outfile, protocol=2)
263
def display(self,npts=1e6,changes=False):
206
def display(self,npts=4e5,changes=False):
264
207
'''Shows a representation of the map in a gui'''
265
from visualiser.robotvisualiser import Visualiser
208
import visualiser.robotvisualiser
266
209
if self._rv is None:
267
210
self._rv_changes = changes
268
self._rv = Visualiser(npts=npts, Rotate=self._rotate)
211
self._rv = visualiser.robotvisualiser.Visualiser(npts=npts)
269
212
self._rv.set_orthographic(Bool=True)
213
#self._rv.setminmax(np.min(self.get_points().points[:,2]),np.max(self.get_points().points[:,2]))
214
self._rv.setautominmax()
216
#if len(inliers) > 0:
217
# vis.setrightpts(inliers)
218
# vis.setleftpts(outliers)
219
#vis.setspinpts(FreeMapper.get_points())
271
221
# TODO visualiser should take a point cloud object
272
222
self._rv.addmappts(self.get_points().points)
280
def add_scan(self, pc, pose, timestamp=None):
281
'''Adds a new scan to the existing map.
283
Adds the occupied voxels and removes the freespace.
285
userdata = pc.userdata
288
self.clear_freespace(xyzs, pose)
289
self.add_points(xyzs, pose, timestamp, userdata)
291
def clear_freespace(self, xyzs, pose):
292
'''Removes the free space voxels from the occupancy list.
295
xyzs: The occupied PointCloud, with the sensor as origin.
296
pose: The pose of `xyzs` PointCloud with respect to global map.
298
xyzs, return_pose = poseutil.transformPoints(xyzs, pose)
299
sensor_loc = poseutil.transformPoints(np.array([[0, 0, 0]]), pose)
300
self.mrol.clear_freespace(xyzs, sensor_loc[0])
302
230
def add_points(self, xyzs, pose, timestamp=None, userdata=None):
303
231
'''Adds the pointcloud xyzs to the map, transforming them by the given
304
232
pose if required. Returns the overlap of the new points against the
306
if isinstance(xyzs, pointcloud.PointCloud):
308
assert xyzs.shape[1] == 3, '3 dimensional points'
235
if xyzs.shape[1] > 3: # handling for additional information that is part of the pointcloud object (e.g. colour)
237
userdata = xyzs[:,3:]
239
userdata = np.hstack([xyzs[:,3:],userdata])
309
241
xyzs, return_pose = poseutil.transformPoints(xyzs, pose)
310
242
self.trajectory.append(return_pose)
311
243
if self._rv is not None: # visualisation
312
244
finest = self.mrol.getfinest()
313
245
# determine unique new voxels convert to points and add to the map
314
#ol = occupiedlist.OccupiedList(finest.resolution)
318
vox = ol.getvoxeldata()
319
self._rv.addmappts(vox)
246
ol = occupiedlist.OccupiedList(finest.resolution)
249
self._rv.addmappts(ol.getpoints())
320
250
#self._rv.setminmax(np.min(self.get_points().points[:,2]),np.max(self.get_points().points[:,2]))
321
251
self._rv.setrobotpose(return_pose.getMatrix())
322
252
self._rv.addtrajectorypoint(return_pose.getTuple()[0:3])
427
357
freespace_ol.add_points(outliers)
428
358
freespace_ol.removepoints(inliers)
429
359
return freespace_ol
361
def generate_freespace2(self,pc=None, pose=None, resolution=None, minR=0.5, maxR=5):
363
Uses existing ray-trace method to get
364
around the current limitations of above implementation.
366
Generates an occuplied list consisting of the observed free space at
367
the specified resolution. This is useful for detecting object removal
370
if resolution == None:
371
res = self.get_resolution()
374
assert res >= self.get_resolution()
375
free_space_ol = occupiedlist.OccupiedList(res, maxvoxels=2e6)
376
if pc==None and pose==None:
377
# generate from map using pose list
378
if self._sensor_pts is None:
379
sensor_model_ol = _build_Kinect_sensor_model(res, minR=minR, maxR=maxR)
380
self._sensor_pts = pointcloud.PointCloud(sensor_model_ol.getpoints())
381
print "Sensor model points:", len(self._sensor_pts)
382
base_ol = self.mrol.getfinest()
384
for pose in self.trajectory:
386
inliers, outliers = base_ol.segment_ovl(pose, self._sensor_pts.points)
387
inlier_pc = pointcloud.PointCloud(inliers)
388
free_pts = self.free_space_ray_trace(inliers,pose.getTuple()[:3],self.get_resolution())
389
free_space_ol.add_points(free_pts.points)
391
assert xyzs != None and pose != None, "Both xyzs and pose must be specified"
392
# resampling to align with grid. bad hack, but makes ray_cast easier.
393
voxs = occupiedlist.pointstovoxels(xyzs, res)
394
voxs = quantizer.uniquerows(voxs, 0)
395
X = occupiedlist.voxelstopoints(voxs, res)
396
X_pc = pointcloud.PointCloud(X)
397
free_pts = self.free_space_ray_trace(X_pc,pose.getTuple()[:3],self.get_resolution())
398
free_space_ol.add_points(free_pts.points)
401
def free_space_ray_trace(self,pc,origin,resolution,voxel_offset=0):
402
# This is essentially independant of the VoxelMap; put elsewhere?
405
v_off = np.ceil(voxel_offset*(res/self.get_resolution()))
406
free_pts_OL = occupiedlist.OccupiedList(res,maxvoxels=3e6)
408
for point in pc.points:
409
ray_vox_hits, range2, dir_unit, all_ray_pts = self.ray_trace_pts(origin,point,return_extras=True)
410
all_ray_pts = all_ray_pts.points[:-(v_off+1)]
411
all_ray_vox = occupiedlist.pointstovoxels(all_ray_pts, res)
413
all_ray_vox_ol = occupiedlist.OccupiedList(1)
414
all_ray_vox_ol.add_points(all_ray_vox)
416
ray_vox_hits_ol = occupiedlist.OccupiedList(1)
417
ray_vox_hits_ol.add_points(ray_vox_hits.points)
419
free_ray_vox_ol = all_ray_vox_ol - ray_vox_hits_ol
420
free_pts_OL.add_points(free_ray_vox_ol.getpoints()*res)
422
#if np.remainder(count / len(pc) * 100,1) == 0:
423
# print count / len(pc) * 100 , '%'
424
return pointcloud.PointCloud(free_pts_OL.getpoints())
428
def ray_trace(self, pose, length, axis=0):
429
'''perform a ray-trace from the pose and return all map voxels at the
430
base resolution along the ray as a pointcloud object.
432
return pointcloud.PointCloud(self.mrol.raytrace(pose, length, axis=axis))
434
def ray_trace_pts(self, point1, point2, return_extras=False):
436
perform a ray-trace from point1 to point2 and return all map
437
voxels at the base resolution along the ray as a pointcloud object.
439
if not return_extras:
440
ray_vox_hits = self.mrol.raytracepts(point1, point2, return_extras=False)
441
return pointcloud.PointCloud(ray_vox_hits)
443
ray_vox_hits, range2, dir_unit, all_ray_pts = self.mrol.raytracepts(point1, point2, return_extras=True)
444
return pointcloud.PointCloud(ray_vox_hits), range2, dir_unit, pointcloud.PointCloud(all_ray_pts)
431
446
def _trajectory_objective(self, guess_pose, pts, foo):
432
447
return -occupiedlist.calccollisions(guess_pose, self._traj_alignment_voxel_set, self.res, pts)